From 6fdaab6a3afeb7bb8cfb2752673f6f036e03b9f7 Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 20 Nov 2022 19:33:03 +0100 Subject: [PATCH 01/14] Refactor error message handling - Revise string interpolators; `em` and `exm` nor produce messages, whereas `i`, `e`, and `ex` produce strings. - Use Message intead of () => String in some places. --- .../tools/backend/sjs/JSExportsGen.scala | 7 +- .../src/dotty/tools/dotc/ast/Desugar.scala | 2 +- compiler/src/dotty/tools/dotc/ast/tpd.scala | 4 +- .../src/dotty/tools/dotc/cc/CaptureSet.scala | 2 +- .../src/dotty/tools/dotc/core/Contexts.scala | 2 +- .../dotty/tools/dotc/core/Decorators.scala | 32 +- .../dotty/tools/dotc/core/Denotations.scala | 6 +- .../tools/dotc/core/SymDenotations.scala | 12 +- .../dotc/core/classfile/ClassfileParser.scala | 12 +- .../dotc/core/tasty/PositionPickler.scala | 3 +- .../tools/dotc/core/tasty/TreeUnpickler.scala | 8 +- .../tools/dotc/fromtasty/ReadTasty.scala | 2 +- .../dotty/tools/dotc/parsing/Parsers.scala | 4 +- .../dotty/tools/dotc/quoted/Interpreter.scala | 4 +- .../tools/dotc/quoted/PickledQuotes.scala | 4 +- compiler/src/dotty/tools/dotc/report.scala | 6 + .../dotty/tools/dotc/reporting/messages.scala | 1813 ++++++++--------- .../tools/dotc/transform/CheckReentrant.scala | 4 +- .../dotty/tools/dotc/transform/Erasure.scala | 5 +- .../dotty/tools/dotc/transform/Pickler.scala | 10 +- .../tools/dotc/transform/TreeChecker.scala | 4 +- .../dotty/tools/dotc/typer/Applications.scala | 2 +- .../src/dotty/tools/dotc/typer/Checking.scala | 20 +- .../tools/dotc/typer/ErrorReporting.scala | 16 +- .../dotty/tools/dotc/typer/Implicits.scala | 28 +- .../src/dotty/tools/dotc/typer/Namer.scala | 2 +- .../tools/dotc/typer/QuotesAndSplices.scala | 2 +- .../dotty/tools/dotc/typer/RefChecks.scala | 8 +- .../dotty/tools/dotc/typer/TypeAssigner.scala | 8 +- .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- .../tools/dotc/StringFormatterTest.scala | 28 +- 31 files changed, 1046 insertions(+), 1016 deletions(-) diff --git a/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala b/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala index 0884ec19b53e..cac81c04ea93 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala @@ -135,8 +135,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) { for ((info, _) <- tups.tail) { report.error( - em"export overload conflicts with export of $firstSym: " + - "a field may not share its exported name with another export", + em"export overload conflicts with export of $firstSym: a field may not share its exported name with another export", info.pos) } @@ -264,8 +263,8 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) { .alternatives assert(!alts.isEmpty, - em"Ended up with no alternatives for ${classSym.fullName}::$name. " + - em"Original set was ${alts} with types ${alts.map(_.info)}") + em"""Ended up with no alternatives for ${classSym.fullName}::$name. + |Original set was ${alts} with types ${alts.map(_.info)}""") val (jsName, isProp) = exportNameInfo(name) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 1e1db19bcf25..e8aef6e90fee 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1137,7 +1137,7 @@ object desugar { def errorOnGivenBinding(bind: Bind)(using Context): Boolean = report.error( em"""${hl("given")} patterns are not allowed in a ${hl("val")} definition, - |please bind to an identifier and use an alias given.""".stripMargin, bind) + |please bind to an identifier and use an alias given.""", bind) false def isTuplePattern(arity: Int): Boolean = pat match { diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 52325e36037d..12f216ed87fa 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -428,7 +428,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { else val res = Select(TypeTree(pre), tp) if needLoad && !res.symbol.isStatic then - throw new TypeError(em"cannot establish a reference to $res") + throw new TypeError(e"cannot establish a reference to $res") res def ref(sym: Symbol)(using Context): Tree = @@ -1296,7 +1296,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { else if (tree.tpe.widen isRef numericCls) tree else { - report.warning(i"conversion from ${tree.tpe.widen} to ${numericCls.typeRef} will always fail at runtime.") + report.warning(em"conversion from ${tree.tpe.widen} to ${numericCls.typeRef} will always fail at runtime.") Throw(New(defn.ClassCastExceptionClass.typeRef, Nil)).withSpan(tree.span) } } diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala index 6bf6d7770d8b..3acef85dcc2b 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala @@ -559,7 +559,7 @@ object CaptureSet: // elements from variable sources in contra- and non-variant positions. In essence, // we approximate types resulting from such maps by returning a possible super type // from the actual type. But this is neither sound nor complete. - report.warning(i"trying to add elems ${CaptureSet(newElems)} from unrecognized source $origin of mapped set $this$whereCreated") + report.warning(em"trying to add elems ${CaptureSet(newElems)} from unrecognized source $origin of mapped set $this$whereCreated") CompareResult.fail(this) else CompareResult.OK diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index f5c1235a8106..f920027032a7 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -255,7 +255,7 @@ object Contexts { file catch case ex: InvalidPathException => - report.error(s"invalid file path: ${ex.getMessage}") + report.error(em"invalid file path: ${ex.getMessage}") NoAbstractFile /** AbstractFile with given path, memoized */ diff --git a/compiler/src/dotty/tools/dotc/core/Decorators.scala b/compiler/src/dotty/tools/dotc/core/Decorators.scala index 54faf9a41177..249f2707788c 100644 --- a/compiler/src/dotty/tools/dotc/core/Decorators.scala +++ b/compiler/src/dotty/tools/dotc/core/Decorators.scala @@ -59,7 +59,7 @@ object Decorators { end extension extension (str: => String) - def toMessage: Message = reporting.NoExplanation(str) + def toMessage: Message = NoExplanation(str) /** Implements a findSymbol method on iterators of Symbols that * works like find but avoids Option, replacing None with NoSymbol. @@ -291,17 +291,41 @@ object Decorators { def derivedCons(x1: T, xs1: List[T]) = if (xs.head eq x1) && (xs.tail eq xs1) then xs else x1 :: xs1 + /* This extension defines the following string formatters: + * i, e, ex, em, exm + * String formatters ending in `m` produce a message, the others produce a string. + * Details are as follows: + * + * i : Gneral purpose string interpolator that uses Formtting.show to print compiler data + * e : Like `i`, but with logic that limits the size of messages and that + * filters out nonsensical messages after the first error is reported. + * `e` or its variants should be used for error messages and warnings instead of `i`. + * ex : Like `e`, but it assembles additional info on type variables and for + * disambiguating symbols. + * em : Like `e`, but producing a message + * exm: Like `ex`, but producing a message + */ extension (sc: StringContext) /** General purpose string formatting */ def i(args: Shown*)(using Context): String = new StringFormatter(sc).assemble(args) - /** Formatting for error messages: Like `i` but suppress follow-on - * error messages after the first one if some of their arguments are "non-sensical". + /** Formatting for error messages: Like `i`, with two modifications + * - limit size of messages + * - mark some parts of messages with tags, so that + * error messages after the first one are filtered out if some of + * their arguments are "non-sensical". */ - def em(args: Shown*)(using Context): String = + def e(args: Shown*)(using Context): String = forErrorMessages(new StringFormatter(sc).assemble(args)) + /** A NoExplanation message formatted with `e` */ + def em(args: Shown*)(using Context): NoExplanation = + NoExplanation(e(args*)) + + def exm(args: Shown*)(using Context): NoExplanation = + NoExplanation(ex(args*)) + /** Formatting with added explanations: Like `em`, but add explanations to * give more info about type variables and to disambiguate where needed. */ diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index f267e6c85e03..a56bdac67952 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -1268,9 +1268,9 @@ object Denotations { if sd1.exists then if sd2.exists then throw TypeError( - em"""Failure to disambiguate overloaded reference with - | ${denot1.symbol.showLocated}: ${denot1.info} and - | ${denot2.symbol.showLocated}: ${denot2.info}""") + e"""Failure to disambiguate overloaded reference with + | ${denot1.symbol.showLocated}: ${denot1.info} and + | ${denot2.symbol.showLocated}: ${denot2.info}""") else sd1 else sd2 } diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index a4f1bf3c5e80..f77bca95007f 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -2438,10 +2438,10 @@ object SymDenotations { val youngest = assocFiles.filter(_.lastModified == lastModDate) val chosen = youngest.head def ambiguousFilesMsg(f: AbstractFile) = - em"""Toplevel definition $name is defined in - | $chosen - |and also in - | $f""" + e"""Toplevel definition $name is defined in + | $chosen + |and also in + | $f""" if youngest.size > 1 then throw TypeError(i"""${ambiguousFilesMsg(youngest.tail.head)} |One of these files should be removed from the classpath.""") @@ -2454,8 +2454,8 @@ object SymDenotations { try f.container == chosen.container catch case NonFatal(ex) => true if !ambiguityWarningIssued then for conflicting <- assocFiles.find(!sameContainer(_)) do - report.warning(i"""${ambiguousFilesMsg(conflicting.nn)} - |Keeping only the definition in $chosen""") + report.warning(em"""${ambiguousFilesMsg(conflicting.nn)} + |Keeping only the definition in $chosen""") ambiguityWarningIssued = true multi.filterWithPredicate(_.symbol.associatedFile == chosen) end dropStale diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 33a1e1dd6e73..ad74b374a0be 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -326,7 +326,7 @@ class ClassfileParser( if (isEnum) { val enumClass = sym.owner.linkedClass if (!enumClass.exists) - report.warning(s"no linked class for java enum $sym in ${sym.owner}. A referencing class file might be missing an InnerClasses entry.") + report.warning(em"no linked class for java enum $sym in ${sym.owner}. A referencing class file might be missing an InnerClasses entry.") else { if (!enumClass.is(Flags.Sealed)) enumClass.setFlag(Flags.AbstractSealed) enumClass.addAnnotation(Annotation.Child(sym, NoSpan)) @@ -656,7 +656,7 @@ class ClassfileParser( case tp: TypeRef if tp.denot.infoOrCompleter.isInstanceOf[StubInfo] => // Silently ignore missing annotation classes like javac if ctx.debug then - report.warning(i"Error while parsing annotations in ${classfile}: annotation class $tp not present on classpath") + report.warning(em"Error while parsing annotations in ${classfile}: annotation class $tp not present on classpath") None case _ => if (hasError || skip) None @@ -671,7 +671,7 @@ class ClassfileParser( // the classpath would *not* end up here. A class not found is signaled // with a `FatalError` exception, handled above. Here you'd end up after a NPE (for example), // and that should never be swallowed silently. - report.warning("Caught: " + ex + " while parsing annotations in " + classfile) + report.warning(em"Caught: $ex while parsing annotations in $classfile") if (ctx.debug) ex.printStackTrace() None // ignore malformed annotations @@ -753,7 +753,7 @@ class ClassfileParser( case tpnme.ConstantValueATTR => val c = pool.getConstant(in.nextChar) if (c ne null) res.constant = c - else report.warning(s"Invalid constant in attribute of ${sym.showLocated} while parsing ${classfile}") + else report.warning(em"Invalid constant in attribute of ${sym.showLocated} while parsing ${classfile}") case tpnme.MethodParametersATTR => val paramCount = in.nextByte @@ -967,7 +967,7 @@ class ClassfileParser( } } else { - report.error(s"Could not find $path in ${classfile.underlyingSource}") + report.error(em"Could not find $path in ${classfile.underlyingSource}") Array.empty } case _ => @@ -975,7 +975,7 @@ class ClassfileParser( val name = classfile.name.stripSuffix(".class") + ".tasty" val tastyFileOrNull = dir.lookupName(name, false) if (tastyFileOrNull == null) { - report.error(s"Could not find TASTY file $name under $dir") + report.error(em"Could not find TASTY file $name under $dir") Array.empty } else tastyFileOrNull.toByteArray diff --git a/compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala index ad0c051e1b7b..e8946c8b4037 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala @@ -14,6 +14,7 @@ import core._ import Annotations._, Decorators._ import collection.mutable import util.Spans._ +import reporting.Message class PositionPickler( pickler: TastyPickler, @@ -33,7 +34,7 @@ class PositionPickler( (addrDelta << 3) | (toInt(hasStartDelta) << 2) | (toInt(hasEndDelta) << 1) | toInt(hasPoint) } - def picklePositions(source: SourceFile, roots: List[Tree], warnings: mutable.ListBuffer[String]): Unit = { + def picklePositions(source: SourceFile, roots: List[Tree], warnings: mutable.ListBuffer[Message]): Unit = { /** Pickle the number of lines followed by the length of each line */ def pickleLineOffsets(): Unit = { val content = source.content() diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 617a2c55a7ad..78ffaaacd452 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -127,10 +127,10 @@ class TreeUnpickler(reader: TastyReader, if f == null then "" else s" in $f" if ctx.settings.YdebugUnpickling.value then throw ex else throw TypeError( - em"""Could not read definition of $denot$where - |An exception was encountered: - | $ex - |Run with -Ydebug-unpickling to see full stack trace.""") + e"""Could not read definition of $denot$where + |An exception was encountered: + | $ex + |Run with -Ydebug-unpickling to see full stack trace.""") treeAtAddr(currentAddr) = try atPhaseBeforeTransforms { diff --git a/compiler/src/dotty/tools/dotc/fromtasty/ReadTasty.scala b/compiler/src/dotty/tools/dotc/fromtasty/ReadTasty.scala index 864f5277bff3..86ae99b3e0f9 100644 --- a/compiler/src/dotty/tools/dotc/fromtasty/ReadTasty.scala +++ b/compiler/src/dotty/tools/dotc/fromtasty/ReadTasty.scala @@ -29,7 +29,7 @@ class ReadTasty extends Phase { val className = unit.className.toTypeName def cannotUnpickle(reason: String): None.type = { - report.error(s"class $className cannot be unpickled because $reason") + report.error(em"class $className cannot be unpickled because $reason") None } diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index a198cccc85cc..7a36523e87bf 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1743,7 +1743,7 @@ object Parsers { Ident(tpnme.USCOREkw).withSpan(Span(start, in.lastOffset, start)) else if sourceVersion.isAtLeast(future) then - deprecationWarning(em"`_` is deprecated for wildcard arguments of types: use `?` instead".toMessage) + deprecationWarning(em"`_` is deprecated for wildcard arguments of types: use `?` instead") patch(source, Span(in.offset, in.offset + 1), "?") val start = in.skipToken() typeBounds().withSpan(Span(start, in.lastOffset, start)) @@ -3488,7 +3488,7 @@ object Parsers { if sourceVersion.isAtLeast(future) then deprecationWarning( em"""`= _` has been deprecated; use `= uninitialized` instead. - |`uninitialized` can be imported with `scala.compiletime.uninitialized`.""".toMessage, + |`uninitialized` can be imported with `scala.compiletime.uninitialized`.""", rhsOffset) placeholderParams = placeholderParams.tail atSpan(rhs0.span) { Ident(nme.WILDCARD) } diff --git a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala index 5a9490c3723e..0d4afefd8fab 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala @@ -60,7 +60,7 @@ abstract class Interpreter(pos: SrcPos, classLoader: ClassLoader)(using Context) case tree: Ident if tree.symbol.is(Inline, butNot = Method) => tree.tpe.widenTermRefExpr match case ConstantType(c) => c.value.asInstanceOf[Object] - case _ => throw new StopInterpretation(em"${tree.symbol} could not be inlined", tree.srcPos) + case _ => throw new StopInterpretation(e"${tree.symbol} could not be inlined", tree.srcPos) // TODO disallow interpreted method calls as arguments case Call(fn, args) => @@ -231,7 +231,7 @@ abstract class Interpreter(pos: SrcPos, classLoader: ClassLoader)(using Context) try clazz.getMethod(name.toString, paramClasses: _*) catch { case _: NoSuchMethodException => - val msg = em"Could not find method ${clazz.getCanonicalName}.$name with parameters ($paramClasses%, %)" + val msg = e"Could not find method ${clazz.getCanonicalName}.$name with parameters ($paramClasses%, %)" throw new StopInterpretation(msg, pos) case MissingClassDefinedInCurrentRun(sym) if ctx.compilationUnit.isSuspendable => if (ctx.settings.XprintSuspension.value) diff --git a/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala index 41f3fd4f64f3..614ec8b11c2e 100644 --- a/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala @@ -12,7 +12,7 @@ import dotty.tools.dotc.core.tasty.{ PositionPickler, TastyPickler, TastyPrinter import dotty.tools.dotc.core.tasty.DottyUnpickler import dotty.tools.dotc.core.tasty.TreeUnpickler.UnpickleMode import dotty.tools.dotc.report - +import dotty.tools.dotc.reporting.Message import scala.quoted.Quotes import scala.quoted.runtime.impl._ @@ -220,7 +220,7 @@ object PickledQuotes { treePkl.pickle(tree :: Nil) treePkl.compactify() if tree.span.exists then - val positionWarnings = new mutable.ListBuffer[String]() + val positionWarnings = new mutable.ListBuffer[Message]() val reference = ctx.settings.sourceroot.value new PositionPickler(pickler, treePkl.buf.addrOfTree, treePkl.treeAnnots, reference) .picklePositions(ctx.compilationUnit.source, tree :: Nil, positionWarnings) diff --git a/compiler/src/dotty/tools/dotc/report.scala b/compiler/src/dotty/tools/dotc/report.scala index 00399ecbfd0a..2c2155e9c162 100644 --- a/compiler/src/dotty/tools/dotc/report.scala +++ b/compiler/src/dotty/tools/dotc/report.scala @@ -70,6 +70,9 @@ object report: def warning(msg: Message, pos: SrcPos)(using Context): Unit = issueWarning(new Warning(msg, addInlineds(pos))) + def warning(msg: Message)(using Context): Unit = + warning(msg, NoSourcePosition) + def warning(msg: => String, pos: SrcPos = NoSourcePosition)(using Context): Unit = warning(msg.toMessage, pos) @@ -78,6 +81,9 @@ object report: ctx.reporter.report(new Error(msg, fullPos)) if ctx.settings.YdebugError.value then Thread.dumpStack() + def error(msg: Message)(using Context): Unit = + error(msg, NoSourcePosition) + def error(msg: => String, pos: SrcPos = NoSourcePosition)(using Context): Unit = error(msg.toMessage, pos) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 2197ea63a1c2..94edf63e5487 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -98,55 +98,55 @@ import cc.CaptureSet.IdentityCaptRefMap | // perform your cleanup here! |}""".stripMargin - em"""|A ${hl("try")} expression should be followed by some mechanism to handle any exceptions - |thrown. Typically a ${hl("catch")} expression follows the ${hl("try")} and pattern matches - |on any expected exceptions. For example: - | - |$code1 - | - |It is also possible to follow a ${hl("try")} immediately by a ${hl("finally")} - letting the - |exception propagate - but still allowing for some clean up in ${hl("finally")}: - | - |$code2 - | - |It is recommended to use the ${hl("NonFatal")} extractor to catch all exceptions as it - |correctly handles transfer functions like ${hl("return")}.""" + e"""|A ${hl("try")} expression should be followed by some mechanism to handle any exceptions + |thrown. Typically a ${hl("catch")} expression follows the ${hl("try")} and pattern matches + |on any expected exceptions. For example: + | + |$code1 + | + |It is also possible to follow a ${hl("try")} immediately by a ${hl("finally")} - letting the + |exception propagate - but still allowing for some clean up in ${hl("finally")}: + | + |$code2 + | + |It is recommended to use the ${hl("NonFatal")} extractor to catch all exceptions as it + |correctly handles transfer functions like ${hl("return")}.""" } } class EmptyCatchBlock(tryBody: untpd.Tree)(using Context) extends EmptyCatchOrFinallyBlock(tryBody, EmptyCatchBlockID) { def msg = - em"""|The ${hl("catch")} block does not contain a valid expression, try - |adding a case like - ${hl("case e: Exception =>")} to the block""" + e"""|The ${hl("catch")} block does not contain a valid expression, try + |adding a case like - ${hl("case e: Exception =>")} to the block""" } class EmptyCatchAndFinallyBlock(tryBody: untpd.Tree)(using Context) extends EmptyCatchOrFinallyBlock(tryBody, EmptyCatchAndFinallyBlockID) { def msg = - em"""|A ${hl("try")} without ${hl("catch")} or ${hl("finally")} is equivalent to putting - |its body in a block; no exceptions are handled.""" + e"""|A ${hl("try")} without ${hl("catch")} or ${hl("finally")} is equivalent to putting + |its body in a block; no exceptions are handled.""" } class DeprecatedWithOperator()(using Context) extends SyntaxMsg(DeprecatedWithOperatorID) { def msg = - em"""${hl("with")} as a type operator has been deprecated; use ${hl("&")} instead""" + e"""${hl("with")} as a type operator has been deprecated; use ${hl("&")} instead""" def explain = - em"""|Dotty introduces intersection types - ${hl("&")} types. These replace the - |use of the ${hl("with")} keyword. There are a few differences in - |semantics between intersection types and using ${hl("with")}.""" + e"""|Dotty introduces intersection types - ${hl("&")} types. These replace the + |use of the ${hl("with")} keyword. There are a few differences in + |semantics between intersection types and using ${hl("with")}.""" } class CaseClassMissingParamList(cdef: untpd.TypeDef)(using Context) extends SyntaxMsg(CaseClassMissingParamListID) { def msg = - em"""|A ${hl("case class")} must have at least one parameter list""" + e"""|A ${hl("case class")} must have at least one parameter list""" def explain = - em"""|${cdef.name} must have at least one parameter list, if you would rather - |have a singleton representation of ${cdef.name}, use a "${hl("case object")}". - |Or, add an explicit ${hl("()")} as a parameter list to ${cdef.name}.""" + e"""|${cdef.name} must have at least one parameter list, if you would rather + |have a singleton representation of ${cdef.name}, use a "${hl("case object")}". + |Or, add an explicit ${hl("()")} as a parameter list to ${cdef.name}.""" } class AnonymousFunctionMissingParamType(param: untpd.ValDef, @@ -192,29 +192,28 @@ import cc.CaptureSet.IdentityCaptRefMap | val team = new Team[Int] |} """.stripMargin - em"""|Wildcard on arguments is not allowed when declaring a new type. - | - |Given the following example: - | - |$code1 - | - |You must complete all the type parameters, for instance: - | - |$code2 """ + e"""|Wildcard on arguments is not allowed when declaring a new type. + | + |Given the following example: + | + |$code1 + | + |You must complete all the type parameters, for instance: + | + |$code2 """ } // Type Errors ------------------------------------------------------------ // class DuplicateBind(bind: untpd.Bind, tree: untpd.CaseDef)(using Context) extends NamingMsg(DuplicateBindID) { - def msg = em"duplicate pattern variable: ${bind.name}" + def msg = e"duplicate pattern variable: ${bind.name}" def explain = { val pat = tree.pat.show - val guard = tree.guard match { + val guard = tree.guard match case untpd.EmptyTree => "" case guard => s"if ${guard.show}" - } val body = tree.body match { case Block(Nil, untpd.EmptyTree) => "" @@ -223,22 +222,22 @@ import cc.CaptureSet.IdentityCaptRefMap val caseDef = s"case $pat$guard => $body" - em"""|For each ${hl("case")} bound variable names have to be unique. In: - | - |$caseDef - | - |${bind.name} is not unique. Rename one of the bound variables!""" + e"""|For each ${hl("case")} bound variable names have to be unique. In: + | + |$caseDef + | + |${bind.name} is not unique. Rename one of the bound variables!""" } } class MissingIdent(tree: untpd.Ident, treeKind: String, val name: Name)(using Context) extends NotFoundMsg(MissingIdentID) { - def msg = em"Not found: $treeKind$name" + def msg = e"Not found: $treeKind$name" def explain = { - em"""|The identifier for `$treeKind$name` is not bound, that is, - |no declaration for this identifier can be found. - |That can happen, for example, if `$name` or its declaration has either been - |misspelt or if an import is missing.""" + e"""|The identifier for `$treeKind$name` is not bound, that is, + |no declaration for this identifier can be found. + |That can happen, for example, if `$name` or its declaration has either been + |misspelt or if an import is missing.""" } } @@ -346,8 +345,8 @@ import cc.CaptureSet.IdentityCaptRefMap // an assumption is made here that the values and valueOf methods were not generated // because the enum defines non-singleton cases i""" - |Although ${site.classSymbol.companionClass} is an enum, it has non-singleton cases, - |meaning a $kind is not defined""" + |Although ${site.classSymbol.companionClass} is an enum, it has non-singleton cases, + |meaning a $kind is not defined""" else "" @@ -405,23 +404,23 @@ import cc.CaptureSet.IdentityCaptRefMap | |class C extends Logging(new File("log.data"))""".stripMargin - em"""|Earlier versions of Scala did not support trait parameters and "early - |definitions" (also known as "early initializers") were used as an alternative. - | - |Example of old syntax: - | - |$code1 - | - |The above code can now be written as: - | - |$code2 - |""" + e"""|Earlier versions of Scala did not support trait parameters and "early + |definitions" (also known as "early initializers") were used as an alternative. + | + |Example of old syntax: + | + |$code1 + | + |The above code can now be written as: + | + |$code2 + |""" } } class TopLevelImplicitClass(cdef: untpd.TypeDef)(using Context) extends SyntaxMsg(TopLevelImplicitClassID) { - def msg = em"""An ${hl("implicit class")} may not be top-level""" + def msg = e"""An ${hl("implicit class")} may not be top-level""" def explain = { val TypeDef(name, impl @ Template(constr0, parents, self, _)) = cdef: @unchecked @@ -430,33 +429,33 @@ import cc.CaptureSet.IdentityCaptRefMap else constr0.termParamss(0).map(_.withMods(untpd.Modifiers()).show).mkString(", ") def defHasBody[T] = impl.body.exists(!_.isEmpty) val exampleBody = if (defHasBody) "{\n ...\n }" else "" - em"""|There may not be any method, member or object in scope with the same name as - |the implicit class and a case class automatically gets a companion object with - |the same name created by the compiler which would cause a naming conflict if it - |were allowed. - | | - |To resolve the conflict declare ${cdef.name} inside of an ${hl("object")} then import the class - |from the object at the use site if needed, for example: - | - |object Implicits { - | implicit class ${cdef.name}($exampleArgs)$exampleBody - |} - | - |// At the use site: - |import Implicits.${cdef.name}""" + e"""|There may not be any method, member or object in scope with the same name as + |the implicit class and a case class automatically gets a companion object with + |the same name created by the compiler which would cause a naming conflict if it + |were allowed. + | | + |To resolve the conflict declare ${cdef.name} inside of an ${hl("object")} then import the class + |from the object at the use site if needed, for example: + | + |object Implicits { + | implicit class ${cdef.name}($exampleArgs)$exampleBody + |} + | + |// At the use site: + |import Implicits.${cdef.name}""" } } class ImplicitCaseClass(cdef: untpd.TypeDef)(using Context) extends SyntaxMsg(ImplicitCaseClassID) { - def msg = em"""A ${hl("case class")} may not be defined as ${hl("implicit")}""" + def msg = e"""A ${hl("case class")} may not be defined as ${hl("implicit")}""" def explain = - em"""|Implicit classes may not be case classes. Instead use a plain class: - | - |implicit class ${cdef.name}... - | - |""" + e"""|Implicit classes may not be case classes. Instead use a plain class: + | + |implicit class ${cdef.name}... + | + |""" } class ImplicitClassPrimaryConstructorArity()(using Context) @@ -464,51 +463,51 @@ import cc.CaptureSet.IdentityCaptRefMap def msg = "Implicit classes must accept exactly one primary constructor parameter" def explain = { val example = "implicit class RichDate(date: java.util.Date)" - em"""Implicit classes may only take one non-implicit argument in their constructor. For example: - | - | $example - | - |While it’s possible to create an implicit class with more than one non-implicit argument, - |such classes aren’t used during implicit lookup. - |""" + e"""Implicit classes may only take one non-implicit argument in their constructor. For example: + | + | $example + | + |While it’s possible to create an implicit class with more than one non-implicit argument, + |such classes aren’t used during implicit lookup. + |""" } } class ObjectMayNotHaveSelfType(mdef: untpd.ModuleDef)(using Context) extends SyntaxMsg(ObjectMayNotHaveSelfTypeID) { - def msg = em"""${hl("object")}s must not have a self ${hl("type")}""" + def msg = e"""${hl("object")}s must not have a self ${hl("type")}""" def explain = { val untpd.ModuleDef(name, tmpl) = mdef val ValDef(_, selfTpt, _) = tmpl.self - em"""|${hl("object")}s must not have a self ${hl("type")}: - | - |Consider these alternative solutions: - | - Create a trait or a class instead of an object - | - Let the object extend a trait containing the self type: - | - | object $name extends ${selfTpt.show}""" + e"""|${hl("object")}s must not have a self ${hl("type")}: + | + |Consider these alternative solutions: + | - Create a trait or a class instead of an object + | - Let the object extend a trait containing the self type: + | + | object $name extends ${selfTpt.show}""" } } class RepeatedModifier(modifier: String)(implicit ctx:Context) extends SyntaxMsg(RepeatedModifierID) { - def msg = em"""Repeated modifier $modifier""" + def msg = e"""Repeated modifier $modifier""" def explain = { - val code1 = em"""private private val Origin = Point(0, 0)""" - val code2 = em"""private final val Origin = Point(0, 0)""" - em"""This happens when you accidentally specify the same modifier twice. - | - |Example: - | - |$code1 - | - |instead of - | - |$code2 - | - |""" + val code1 = "private private val Origin = Point(0, 0)" + val code2 = "private final val Origin = Point(0, 0)" + e"""This happens when you accidentally specify the same modifier twice. + | + |Example: + | + |$code1 + | + |instead of + | + |$code2 + | + |""" } } @@ -518,68 +517,68 @@ import cc.CaptureSet.IdentityCaptRefMap def explain = { val code1 = "s\"$new Point(0, 0)\"" val code2 = "s\"${new Point(0, 0)}\"" - em"""|This usually happens when you forget to place your expressions inside curly braces. - | - |$code1 - | - |should be written as - | - |$code2 - |""" + e"""|This usually happens when you forget to place your expressions inside curly braces. + | + |$code1 + | + |should be written as + | + |$code2 + |""" } } class UnboundPlaceholderParameter()(implicit ctx:Context) extends SyntaxMsg(UnboundPlaceholderParameterID) { - def msg = em"""Unbound placeholder parameter; incorrect use of ${hl("_")}""" + def msg = e"""Unbound placeholder parameter; incorrect use of ${hl("_")}""" def explain = - em"""|The ${hl("_")} placeholder syntax was used where it could not be bound. - |Consider explicitly writing the variable binding. - | - |This can be done by replacing ${hl("_")} with a variable (eg. ${hl("x")}) - |and adding ${hl("x =>")} where applicable. - | - |Example before: - | - |${hl("{ _ }")} - | - |Example after: - | - |${hl("x => { x }")} - | - |Another common occurrence for this error is defining a val with ${hl("_")}: - | - |${hl("val a = _")} - | - |But this val definition isn't very useful, it can never be assigned - |another value. And thus will always remain uninitialized. - |Consider replacing the ${hl("val")} with ${hl("var")}: - | - |${hl("var a = _")} - | - |Note that this use of ${hl("_")} is not placeholder syntax, - |but an uninitialized var definition. - |Only fields can be left uninitialized in this manner; local variables - |must be initialized. - | - |Another occurrence for this error is self type definition. - |The ${hl("_")} can be replaced with ${hl("this")}. - | - |Example before: - | - |${hl("trait A { _: B => ... ")} - | - |Example after: - | - |${hl("trait A { this: B => ... ")} - |""" + e"""|The ${hl("_")} placeholder syntax was used where it could not be bound. + |Consider explicitly writing the variable binding. + | + |This can be done by replacing ${hl("_")} with a variable (eg. ${hl("x")}) + |and adding ${hl("x =>")} where applicable. + | + |Example before: + | + |${hl("{ _ }")} + | + |Example after: + | + |${hl("x => { x }")} + | + |Another common occurrence for this error is defining a val with ${hl("_")}: + | + |${hl("val a = _")} + | + |But this val definition isn't very useful, it can never be assigned + |another value. And thus will always remain uninitialized. + |Consider replacing the ${hl("val")} with ${hl("var")}: + | + |${hl("var a = _")} + | + |Note that this use of ${hl("_")} is not placeholder syntax, + |but an uninitialized var definition. + |Only fields can be left uninitialized in this manner; local variables + |must be initialized. + | + |Another occurrence for this error is self type definition. + |The ${hl("_")} can be replaced with ${hl("this")}. + | + |Example before: + | + |${hl("trait A { _: B => ... ")} + | + |Example after: + | + |${hl("trait A { this: B => ... ")} + |""" } class IllegalStartSimpleExpr(illegalToken: String)(using Context) extends SyntaxMsg(IllegalStartSimpleExprID) { - def msg = em"expression expected but ${Red(illegalToken)} found" + def msg = e"expression expected but ${Red(illegalToken)} found" def explain = { - em"""|An expression cannot start with ${Red(illegalToken)}.""" + e"""|An expression cannot start with ${Red(illegalToken)}.""" } } @@ -587,58 +586,58 @@ import cc.CaptureSet.IdentityCaptRefMap extends SyntaxMsg(MissingReturnTypeID) { def msg = "Missing return type" def explain = - em"""|An abstract declaration must have a return type. For example: - | - |trait Shape: - | ${hl("def area: Double")} // abstract declaration returning a Double""" + e"""|An abstract declaration must have a return type. For example: + | + |trait Shape: + | ${hl("def area: Double")} // abstract declaration returning a Double""" } class MissingReturnTypeWithReturnStatement(method: Symbol)(using Context) extends SyntaxMsg(MissingReturnTypeWithReturnStatementID) { - def msg = em"$method has a return statement; it needs a result type" + def msg = e"$method has a return statement; it needs a result type" def explain = - em"""|If a method contains a ${hl("return")} statement, it must have an - |explicit return type. For example: - | - |${hl("def good: Int /* explicit return type */ = return 1")}""" + e"""|If a method contains a ${hl("return")} statement, it must have an + |explicit return type. For example: + | + |${hl("def good: Int /* explicit return type */ = return 1")}""" } class YieldOrDoExpectedInForComprehension()(using Context) extends SyntaxMsg(YieldOrDoExpectedInForComprehensionID) { - def msg = em"${hl("yield")} or ${hl("do")} expected" + def msg = e"${hl("yield")} or ${hl("do")} expected" def explain = - em"""|When the enumerators in a for comprehension are not placed in parentheses or - |braces, a ${hl("do")} or ${hl("yield")} statement is required after the enumerators - |section of the comprehension. - | - |You can save some keystrokes by omitting the parentheses and writing - | - |${hl("val numbers = for i <- 1 to 3 yield i")} - | - | instead of - | - |${hl("val numbers = for (i <- 1 to 3) yield i")} - | - |but the ${hl("yield")} keyword is still required. - | - |For comprehensions that simply perform a side effect without yielding anything - |can also be written without parentheses but a ${hl("do")} keyword has to be - |included. For example, - | - |${hl("for (i <- 1 to 3) println(i)")} - | - |can be written as - | - |${hl("for i <- 1 to 3 do println(i) // notice the 'do' keyword")} - | - |""" + e"""|When the enumerators in a for comprehension are not placed in parentheses or + |braces, a ${hl("do")} or ${hl("yield")} statement is required after the enumerators + |section of the comprehension. + | + |You can save some keystrokes by omitting the parentheses and writing + | + |${hl("val numbers = for i <- 1 to 3 yield i")} + | + | instead of + | + |${hl("val numbers = for (i <- 1 to 3) yield i")} + | + |but the ${hl("yield")} keyword is still required. + | + |For comprehensions that simply perform a side effect without yielding anything + |can also be written without parentheses but a ${hl("do")} keyword has to be + |included. For example, + | + |${hl("for (i <- 1 to 3) println(i)")} + | + |can be written as + | + |${hl("for i <- 1 to 3 do println(i) // notice the 'do' keyword")} + | + |""" } class ProperDefinitionNotFound()(using Context) extends Message(ProperDefinitionNotFoundID) { def kind = MessageKind.DocComment - def msg = em"""Proper definition was not found in ${hl("@usecase")}""" + def msg = e"""Proper definition was not found in ${hl("@usecase")}""" def explain = { val noUsecase = @@ -652,49 +651,49 @@ import cc.CaptureSet.IdentityCaptRefMap |def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That |""".stripMargin - em"""|Usecases are only supported for ${hl("def")}s. They exist because with Scala's - |advanced type-system, we sometimes end up with seemingly scary signatures. - |The usage of these methods, however, needs not be - for instance the ${hl("map")} - |function - | - |${hl("List(1, 2, 3).map(2 * _) // res: List(2, 4, 6)")} - | - |is easy to understand and use - but has a rather bulky signature: - | - |$noUsecase - | - |to mitigate this and ease the usage of such functions we have the ${hl("@usecase")} - |annotation for docstrings. Which can be used like this: - | - |$usecase - | - |When creating the docs, the signature of the method is substituted by the - |usecase and the compiler makes sure that it is valid. Because of this, you're - |only allowed to use ${hl("def")}s when defining usecases.""" + e"""|Usecases are only supported for ${hl("def")}s. They exist because with Scala's + |advanced type-system, we sometimes end up with seemingly scary signatures. + |The usage of these methods, however, needs not be - for instance the ${hl("map")} + |function + | + |${hl("List(1, 2, 3).map(2 * _) // res: List(2, 4, 6)")} + | + |is easy to understand and use - but has a rather bulky signature: + | + |$noUsecase + | + |to mitigate this and ease the usage of such functions we have the ${hl("@usecase")} + |annotation for docstrings. Which can be used like this: + | + |$usecase + | + |When creating the docs, the signature of the method is substituted by the + |usecase and the compiler makes sure that it is valid. Because of this, you're + |only allowed to use ${hl("def")}s when defining usecases.""" } } class ByNameParameterNotSupported(tpe: untpd.Tree)(using Context) extends SyntaxMsg(ByNameParameterNotSupportedID) { - def msg = em"By-name parameter type ${tpe} not allowed here." + def msg = e"By-name parameter type ${tpe} not allowed here." def explain = - em"""|By-name parameters act like functions that are only evaluated when referenced, - |allowing for lazy evaluation of a parameter. - | - |An example of using a by-name parameter would look like: - |${hl("def func(f: => Boolean) = f // 'f' is evaluated when referenced within the function")} - | - |An example of the syntax of passing an actual function as a parameter: - |${hl("def func(f: (Boolean => Boolean)) = f(true)")} - | - |or: - | - |${hl("def func(f: Boolean => Boolean) = f(true)")} - | - |And the usage could be as such: - |${hl("func(bool => // do something...)")} - |""" + e"""|By-name parameters act like functions that are only evaluated when referenced, + |allowing for lazy evaluation of a parameter. + | + |An example of using a by-name parameter would look like: + |${hl("def func(f: => Boolean) = f // 'f' is evaluated when referenced within the function")} + | + |An example of the syntax of passing an actual function as a parameter: + |${hl("def func(f: (Boolean => Boolean)) = f(true)")} + | + |or: + | + |${hl("def func(f: Boolean => Boolean) = f(true)")} + | + |And the usage could be as such: + |${hl("func(bool => // do something...)")} + |""" } class WrongNumberOfTypeArgs(fntpe: Type, expectedArgs: List[ParamInfo], actual: List[untpd.Tree])(using Context) @@ -714,9 +713,9 @@ import cc.CaptureSet.IdentityCaptRefMap case NoSymbol => fntpe.show case symbol => symbol.showFullName catch case NonFatal(ex) => fntpe.show - em"""|$msgPrefix type arguments for $prettyName$expectedArgString - |expected: $expectedArgString - |actual: $actualArgString""".stripMargin + e"""|$msgPrefix type arguments for $prettyName$expectedArgString + |expected: $expectedArgString + |actual: $actualArgString""" def explain = { val tooManyTypeParams = @@ -724,23 +723,23 @@ import cc.CaptureSet.IdentityCaptRefMap |val list: List[(Int, String)] = List(tuple2)""".stripMargin if (actualCount > expectedCount) - em"""|You have supplied too many type parameters - | - |For example List takes a single type parameter (List[A]) - |If you need to hold more types in a list then you need to combine them - |into another data type that can contain the number of types you need, - |In this example one solution would be to use a Tuple: - | - |${tooManyTypeParams}""" + e"""|You have supplied too many type parameters + | + |For example List takes a single type parameter (List[A]) + |If you need to hold more types in a list then you need to combine them + |into another data type that can contain the number of types you need, + |In this example one solution would be to use a Tuple: + | + |${tooManyTypeParams}""" else - em"""|You have not supplied enough type parameters - |If you specify one type parameter then you need to specify every type parameter.""" + e"""|You have not supplied enough type parameters + |If you specify one type parameter then you need to specify every type parameter.""" } } class IllegalVariableInPatternAlternative(name: Name)(using Context) extends SyntaxMsg(IllegalVariableInPatternAlternativeID) { - def msg = em"Illegal variable $name in pattern alternative" + def msg = e"Illegal variable $name in pattern alternative" def explain = { val varInAlternative = """|def g(pair: (Int,Int)): Int = pair match { @@ -755,14 +754,14 @@ import cc.CaptureSet.IdentityCaptRefMap | case _ => 0 |}""".stripMargin - em"""|Variables are not allowed within alternate pattern matches. You can workaround - |this issue by adding additional cases for each alternative. For example, the - |illegal function: - | - |$varInAlternative - |could be implemented by moving each alternative into a separate case: - | - |$fixedVarInAlternative""" + e"""|Variables are not allowed within alternate pattern matches. You can workaround + |this issue by adding additional cases for each alternative. For example, the + |illegal function: + | + |$varInAlternative + |could be implemented by moving each alternative into a separate case: + | + |$fixedVarInAlternative""" } } @@ -770,19 +769,19 @@ import cc.CaptureSet.IdentityCaptRefMap extends SyntaxMsg(IdentifierExpectedID) { def msg = "identifier expected" def explain = { - val wrongIdentifier = em"def foo: $identifier = {...}" - val validIdentifier = em"def foo = {...}" - em"""|An identifier expected, but $identifier found. This could be because - |$identifier is not a valid identifier. As a workaround, the compiler could - |infer the type for you. For example, instead of: - | - |$wrongIdentifier - | - |Write your code like: - | - |$validIdentifier - | - |""" + val wrongIdentifier = e"def foo: $identifier = {...}" + val validIdentifier = e"def foo = {...}" + e"""|An identifier expected, but $identifier found. This could be because + |$identifier is not a valid identifier. As a workaround, the compiler could + |infer the type for you. For example, instead of: + | + |$wrongIdentifier + | + |Write your code like: + | + |$validIdentifier + | + |""" } } @@ -790,36 +789,36 @@ import cc.CaptureSet.IdentityCaptRefMap extends SyntaxMsg(AuxConstructorNeedsNonImplicitParameterID) { def msg = "Auxiliary constructor needs non-implicit parameter list" def explain = - em"""|Only the primary constructor is allowed an ${hl("implicit")} parameter list; - |auxiliary constructors need non-implicit parameter lists. When a primary - |constructor has an implicit argslist, auxiliary constructors that call the - |primary constructor must specify the implicit value. - | - |To resolve this issue check for: - | - Forgotten parenthesis on ${hl("this")} (${hl("def this() = { ... }")}) - | - Auxiliary constructors specify the implicit value - |""" + e"""|Only the primary constructor is allowed an ${hl("implicit")} parameter list; + |auxiliary constructors need non-implicit parameter lists. When a primary + |constructor has an implicit argslist, auxiliary constructors that call the + |primary constructor must specify the implicit value. + | + |To resolve this issue check for: + | - Forgotten parenthesis on ${hl("this")} (${hl("def this() = { ... }")}) + | - Auxiliary constructors specify the implicit value + |""" } class IllegalLiteral()(using Context) extends SyntaxMsg(IllegalLiteralID) { def msg = "Illegal literal" def explain = - em"""|Available literals can be divided into several groups: - | - Integer literals: 0, 21, 0xFFFFFFFF, -42L - | - Floating Point Literals: 0.0, 1e30f, 3.14159f, 1.0e-100, .1 - | - Boolean Literals: true, false - | - Character Literals: 'a', '\u0041', '\n' - | - String Literals: "Hello, World!" - | - null - |""" + e"""|Available literals can be divided into several groups: + | - Integer literals: 0, 21, 0xFFFFFFFF, -42L + | - Floating Point Literals: 0.0, 1e30f, 3.14159f, 1.0e-100, .1 + | - Boolean Literals: true, false + | - Character Literals: 'a', '\u0041', '\n' + | - String Literals: "Hello, World!" + | - null + |""" } class LossyWideningConstantConversion(sourceType: Type, targetType: Type)(using Context) extends Message(LossyWideningConstantConversionID): def kind = MessageKind.LossyConversion - def msg = em"""|Widening conversion from $sourceType to $targetType loses precision. - |Write `.to$targetType` instead.""".stripMargin + def msg = e"""|Widening conversion from $sourceType to $targetType loses precision. + |Write `.to$targetType` instead.""" def explain = "" class PatternMatchExhaustivity(uncoveredFn: => String, hasMore: Boolean)(using Context) @@ -828,29 +827,29 @@ import cc.CaptureSet.IdentityCaptRefMap lazy val uncovered = uncoveredFn def msg = val addendum = if hasMore then "(More unmatched cases are elided)" else "" - em"""|${hl("match")} may not be exhaustive. - | - |It would fail on pattern case: $uncovered - |$addendum""" + e"""|${hl("match")} may not be exhaustive. + | + |It would fail on pattern case: $uncovered + |$addendum""" def explain = - em"""|There are several ways to make the match exhaustive: - | - Add missing cases as shown in the warning - | - If an extractor always return ${hl("Some(...)")}, write ${hl("Some[X]")} for its return type - | - Add a ${hl("case _ => ...")} at the end to match all remaining cases - |""" + e"""|There are several ways to make the match exhaustive: + | - Add missing cases as shown in the warning + | - If an extractor always return ${hl("Some(...)")}, write ${hl("Some[X]")} for its return type + | - Add a ${hl("case _ => ...")} at the end to match all remaining cases + |""" } class UncheckedTypePattern(msgFn: => String)(using Context) extends PatternMatchMsg(UncheckedTypePatternID) { def msg = msgFn def explain = - em"""|Type arguments and type refinements are erased during compile time, thus it's - |impossible to check them at run-time. - | - |You can either replace the type arguments by ${hl("_")} or use `@unchecked`. - |""" + e"""|Type arguments and type refinements are erased during compile time, thus it's + |impossible to check them at run-time. + | + |You can either replace the type arguments by ${hl("_")} or use `@unchecked`. + |""" } class MatchCaseUnreachable()(using Context) @@ -862,7 +861,7 @@ import cc.CaptureSet.IdentityCaptRefMap class MatchCaseOnlyNullWarning()(using Context) extends PatternMatchMsg(MatchCaseOnlyNullWarningID) { - def msg = em"""Unreachable case except for ${hl("null")} (if this is intentional, consider writing ${hl("case null =>")} instead).""" + def msg = e"""Unreachable case except for ${hl("null")} (if this is intentional, consider writing ${hl("case null =>")} instead).""" def explain = "" } @@ -870,48 +869,48 @@ import cc.CaptureSet.IdentityCaptRefMap extends TypeMsg(MatchableWarningID) { def msg = val kind = if pattern then "pattern selector" else "value" - em"""${kind} should be an instance of Matchable,, - |but it has unmatchable type $tp instead""" + e"""${kind} should be an instance of Matchable,, + |but it has unmatchable type $tp instead""" def explain = if pattern then - em"""A value of type $tp cannot be the selector of a match expression - |since it is not constrained to be `Matchable`. Matching on unconstrained - |values is disallowed since it can uncover implementation details that - |were intended to be hidden and thereby can violate paramtetricity laws - |for reasoning about programs. - | - |The restriction can be overridden by appending `.asMatchable` to - |the selector value. `asMatchable` needs to be imported from - |scala.compiletime. Example: - | - | import compiletime.asMatchable - | def f[X](x: X) = x.asMatchable match { ... }""" + e"""A value of type $tp cannot be the selector of a match expression + |since it is not constrained to be `Matchable`. Matching on unconstrained + |values is disallowed since it can uncover implementation details that + |were intended to be hidden and thereby can violate paramtetricity laws + |for reasoning about programs. + | + |The restriction can be overridden by appending `.asMatchable` to + |the selector value. `asMatchable` needs to be imported from + |scala.compiletime. Example: + | + | import compiletime.asMatchable + | def f[X](x: X) = x.asMatchable match { ... }""" else - em"""The value can be converted to a `Matchable` by appending `.asMatchable`. - |`asMatchable` needs to be imported from scala.compiletime.""" + e"""The value can be converted to a `Matchable` by appending `.asMatchable`. + |`asMatchable` needs to be imported from scala.compiletime.""" } class SeqWildcardPatternPos()(using Context) extends SyntaxMsg(SeqWildcardPatternPosID) { - def msg = em"""${hl("*")} can be used only for last argument""" + def msg = e"""${hl("*")} can be used only for last argument""" def explain = { val code = """def sumOfTheFirstTwo(list: List[Int]): Int = list match { | case List(first, second, x*) => first + second | case _ => 0 |}""" - em"""|Sequence wildcard pattern is expected at the end of an argument list. - |This pattern matches any remaining elements in a sequence. - |Consider the following example: - | - |$code - | - |Calling: - | - |${hl("sumOfTheFirstTwo(List(1, 2, 10))")} - | - |would give 3 as a result""" + e"""|Sequence wildcard pattern is expected at the end of an argument list. + |This pattern matches any remaining elements in a sequence. + |Consider the following example: + | + |$code + | + |Calling: + | + |${hl("sumOfTheFirstTwo(List(1, 2, 10))")} + | + |would give 3 as a result""" } } @@ -941,140 +940,140 @@ import cc.CaptureSet.IdentityCaptRefMap | case List(_, second, x:_*) => second | case _ => 0 |}""" - em"""|Simple patterns can be divided into several groups: - |- Variable Patterns: ${hl("case x => ...")}. - | It matches any value, and binds the variable name to that value. - | A special case is the wild-card pattern _ which is treated as if it was a fresh - | variable on each occurrence. - | - |- Typed Patterns: ${hl("case x: Int => ...")} or ${hl("case _: Int => ...")}. - | This pattern matches any value matched by the specified type; it binds the variable - | name to that value. - | - |- Literal Patterns: ${hl("case 123 => ...")} or ${hl("case 'A' => ...")}. - | This type of pattern matches any value that is equal to the specified literal. - | - |- Stable Identifier Patterns: - | - | $sipCode - | - | the match succeeds only if the x argument and the y argument of f are equal. - | - |- Constructor Patterns: - | - | $constructorPatternsCode - | - | The pattern binds all object's fields to the variable names (name and age, in this - | case). - | - |- Tuple Patterns: - | - | $tupplePatternsCode - | - | Calling: - | - | ${hl("""swap(("Luftballons", 99)""")} - | - | would give ${hl("""(99, "Luftballons")""")} as a result. - | - |- Pattern Sequences: - | - | $patternSequencesCode - | - | Calling: - | - | ${hl("getSecondValue(List(1, 10, 2))")} - | - | would give 10 as a result. - | This pattern is possible because a companion object for the List class has a method - | with the following signature: - | - | ${hl("def unapplySeq[A](x: List[A]): Some[List[A]]")} - |""" + e"""|Simple patterns can be divided into several groups: + |- Variable Patterns: ${hl("case x => ...")}. + | It matches any value, and binds the variable name to that value. + | A special case is the wild-card pattern _ which is treated as if it was a fresh + | variable on each occurrence. + | + |- Typed Patterns: ${hl("case x: Int => ...")} or ${hl("case _: Int => ...")}. + | This pattern matches any value matched by the specified type; it binds the variable + | name to that value. + | + |- Literal Patterns: ${hl("case 123 => ...")} or ${hl("case 'A' => ...")}. + | This type of pattern matches any value that is equal to the specified literal. + | + |- Stable Identifier Patterns: + | + | $sipCode + | + | the match succeeds only if the x argument and the y argument of f are equal. + | + |- Constructor Patterns: + | + | $constructorPatternsCode + | + | The pattern binds all object's fields to the variable names (name and age, in this + | case). + | + |- Tuple Patterns: + | + | $tupplePatternsCode + | + | Calling: + | + | ${hl("""swap(("Luftballons", 99)""")} + | + | would give ${hl("""(99, "Luftballons")""")} as a result. + | + |- Pattern Sequences: + | + | $patternSequencesCode + | + | Calling: + | + | ${hl("getSecondValue(List(1, 10, 2))")} + | + | would give 10 as a result. + | This pattern is possible because a companion object for the List class has a method + | with the following signature: + | + | ${hl("def unapplySeq[A](x: List[A]): Some[List[A]]")} + |""" } } class PkgDuplicateSymbol(existing: Symbol)(using Context) extends NamingMsg(PkgDuplicateSymbolID) { - def msg = em"Trying to define package with same name as $existing" + def msg = e"Trying to define package with same name as $existing" def explain = "" } class ExistentialTypesNoLongerSupported()(using Context) extends SyntaxMsg(ExistentialTypesNoLongerSupportedID) { def msg = - em"""|Existential types are no longer supported - - |use a wildcard or dependent type instead""" + e"""|Existential types are no longer supported - + |use a wildcard or dependent type instead""" def explain = - em"""|The use of existential types is no longer supported. - | - |You should use a wildcard or dependent type instead. - | - |For example: - | - |Instead of using ${hl("forSome")} to specify a type variable - | - |${hl("List[T forSome { type T }]")} - | - |Try using a wildcard type variable - | - |${hl("List[?]")} - |""" + e"""|The use of existential types is no longer supported. + | + |You should use a wildcard or dependent type instead. + | + |For example: + | + |Instead of using ${hl("forSome")} to specify a type variable + | + |${hl("List[T forSome { type T }]")} + | + |Try using a wildcard type variable + | + |${hl("List[?]")} + |""" } class UnboundWildcardType()(using Context) extends SyntaxMsg(UnboundWildcardTypeID) { def msg = "Unbound wildcard type" def explain = - em"""|The wildcard type syntax (${hl("_")}) was used where it could not be bound. - |Replace ${hl("_")} with a non-wildcard type. If the type doesn't matter, - |try replacing ${hl("_")} with ${hl("Any")}. - | - |Examples: - | - |- Parameter lists - | - | Instead of: - | ${hl("def foo(x: _) = ...")} - | - | Use ${hl("Any")} if the type doesn't matter: - | ${hl("def foo(x: Any) = ...")} - | - |- Type arguments - | - | Instead of: - | ${hl("val foo = List[?](1, 2)")} - | - | Use: - | ${hl("val foo = List[Int](1, 2)")} - | - |- Type bounds - | - | Instead of: - | ${hl("def foo[T <: _](x: T) = ...")} - | - | Remove the bounds if the type doesn't matter: - | ${hl("def foo[T](x: T) = ...")} - | - |- ${hl("val")} and ${hl("def")} types - | - | Instead of: - | ${hl("val foo: _ = 3")} - | - | Use: - | ${hl("val foo: Int = 3")} - |""" + e"""|The wildcard type syntax (${hl("_")}) was used where it could not be bound. + |Replace ${hl("_")} with a non-wildcard type. If the type doesn't matter, + |try replacing ${hl("_")} with ${hl("Any")}. + | + |Examples: + | + |- Parameter lists + | + | Instead of: + | ${hl("def foo(x: _) = ...")} + | + | Use ${hl("Any")} if the type doesn't matter: + | ${hl("def foo(x: Any) = ...")} + | + |- Type arguments + | + | Instead of: + | ${hl("val foo = List[?](1, 2)")} + | + | Use: + | ${hl("val foo = List[Int](1, 2)")} + | + |- Type bounds + | + | Instead of: + | ${hl("def foo[T <: _](x: T) = ...")} + | + | Remove the bounds if the type doesn't matter: + | ${hl("def foo[T](x: T) = ...")} + | + |- ${hl("val")} and ${hl("def")} types + | + | Instead of: + | ${hl("val foo: _ = 3")} + | + | Use: + | ${hl("val foo: Int = 3")} + |""" } class OverridesNothing(member: Symbol)(using Context) extends DeclarationMsg(OverridesNothingID) { - def msg = em"""${member} overrides nothing""" + def msg = e"""${member} overrides nothing""" def explain = - em"""|There must be a field or method with the name ${member.name} in a super - |class of ${member.owner} to override it. Did you misspell it? - |Are you extending the right classes? - |""" + e"""|There must be a field or method with the name ${member.name} in a super + |class of ${member.owner} to override it. Did you misspell it? + |Are you extending the right classes? + |""" } class OverridesNothingButNameExists(member: Symbol, existing: List[Denotations.SingleDenotation])(using Context) @@ -1084,18 +1083,18 @@ import cc.CaptureSet.IdentityCaptRefMap if !existing.exists(_.symbol.hasTargetName(member.targetName)) then "target name" else "signature" - em"""${member} has a different $what than the overridden declaration""" + e"""${member} has a different $what than the overridden declaration""" def explain = val existingDecl: String = existing.map(_.showDcl).mkString(" \n") - em"""|There must be a non-final field or method with the name ${member.name} and the - |same parameter list in a super class of ${member.owner} to override it. - | - | ${member.showDcl} - | - |The super classes of ${member.owner} contain the following members - |named ${member.name}: - | ${existingDecl} - |""" + e"""|There must be a non-final field or method with the name ${member.name} and the + |same parameter list in a super class of ${member.owner} to override it. + | + | ${member.showDcl} + | + |The super classes of ${member.owner} contain the following members + |named ${member.name}: + | ${existingDecl} + |""" } class OverrideError(override val msg: String) extends DeclarationMsg(OverrideErrorID): @@ -1108,20 +1107,20 @@ import cc.CaptureSet.IdentityCaptRefMap class ForwardReferenceExtendsOverDefinition(value: Symbol, definition: Symbol)(using Context) extends ReferenceMsg(ForwardReferenceExtendsOverDefinitionID) { - def msg = em"${definition.name} is a forward reference extending over the definition of ${value.name}" + def msg = e"${definition.name} is a forward reference extending over the definition of ${value.name}" def explain = - em"""|${definition.name} is used before you define it, and the definition of ${value.name} - |appears between that use and the definition of ${definition.name}. - | - |Forward references are allowed only, if there are no value definitions between - |the reference and the referred method definition. - | - |Define ${definition.name} before it is used, - |or move the definition of ${value.name} so it does not appear between - |the declaration of ${definition.name} and its use, - |or define ${value.name} as lazy. - |""".stripMargin + e"""|${definition.name} is used before you define it, and the definition of ${value.name} + |appears between that use and the definition of ${definition.name}. + | + |Forward references are allowed only, if there are no value definitions between + |the reference and the referred method definition. + | + |Define ${definition.name} before it is used, + |or move the definition of ${value.name} so it does not appear between + |the declaration of ${definition.name} and its use, + |or define ${value.name} as lazy. + |""" } class ExpectedTokenButFound(expected: Token, found: Token)(using Context) @@ -1133,7 +1132,7 @@ import cc.CaptureSet.IdentityCaptRefMap val expectedText = if (Tokens.isIdentifier(expected)) "an identifier" else Tokens.showToken(expected) - em"""${expectedText} expected, but ${foundText} found""" + e"""${expectedText} expected, but ${foundText} found""" def explain = if (Tokens.isIdentifier(expected) && Tokens.isKeyword(found)) @@ -1148,7 +1147,7 @@ import cc.CaptureSet.IdentityCaptRefMap def msg = val op1Asso: String = if (op2LeftAssoc) "which is right-associative" else "which is left-associative" val op2Asso: String = if (op2LeftAssoc) "which is left-associative" else "which is right-associative" - em"${op1} (${op1Asso}) and ${op2} ($op2Asso) have same precedence and may not be mixed" + e"${op1} (${op1Asso}) and ${op2} ($op2Asso) have same precedence and may not be mixed" def explain = s"""|The operators ${op1} and ${op2} are used as infix operators in the same expression, |but they bind to different sides: @@ -1180,71 +1179,71 @@ import cc.CaptureSet.IdentityCaptRefMap class CantInstantiateAbstractClassOrTrait(cls: Symbol, isTrait: Boolean)(using Context) extends TypeMsg(CantInstantiateAbstractClassOrTraitID) { private val traitOrAbstract = if (isTrait) "a trait" else "abstract" - def msg = em"""${cls.name} is ${traitOrAbstract}; it cannot be instantiated""" + def msg = e"""${cls.name} is ${traitOrAbstract}; it cannot be instantiated""" def explain = - em"""|Abstract classes and traits need to be extended by a concrete class or object - |to make their functionality accessible. - | - |You may want to create an anonymous class extending ${cls.name} with - | ${s"class ${cls.name} { }"} - | - |or add a companion object with - | ${s"object ${cls.name} extends ${cls.name}"} - | - |You need to implement any abstract members in both cases. - |""".stripMargin + e"""|Abstract classes and traits need to be extended by a concrete class or object + |to make their functionality accessible. + | + |You may want to create an anonymous class extending ${cls.name} with + | ${s"class ${cls.name} { }"} + | + |or add a companion object with + | ${s"object ${cls.name} extends ${cls.name}"} + | + |You need to implement any abstract members in both cases. + |""" } class UnreducibleApplication(tycon: Type)(using Context) extends TypeMsg(UnreducibleApplicationID): - def msg = em"unreducible application of higher-kinded type $tycon to wildcard arguments" + def msg = e"unreducible application of higher-kinded type $tycon to wildcard arguments" def explain = - em"""|An abstract type constructor cannot be applied to wildcard arguments. - |Such applications are equivalent to existential types, which are not - |supported in Scala 3.""" + e"""|An abstract type constructor cannot be applied to wildcard arguments. + |Such applications are equivalent to existential types, which are not + |supported in Scala 3.""" class OverloadedOrRecursiveMethodNeedsResultType(cycleSym: Symbol)(using Context) extends CyclicMsg(OverloadedOrRecursiveMethodNeedsResultTypeID) { - def msg = em"""Overloaded or recursive $cycleSym needs return type""" + def msg = e"""Overloaded or recursive $cycleSym needs return type""" def explain = - em"""Case 1: $cycleSym is overloaded - |If there are multiple methods named $cycleSym and at least one definition of - |it calls another, you need to specify the calling method's return type. - | - |Case 2: $cycleSym is recursive - |If $cycleSym calls itself on any path (even through mutual recursion), you need to specify the return type - |of $cycleSym or of a definition it's mutually recursive with. - |""".stripMargin + e"""Case 1: $cycleSym is overloaded + |If there are multiple methods named $cycleSym and at least one definition of + |it calls another, you need to specify the calling method's return type. + | + |Case 2: $cycleSym is recursive + |If $cycleSym calls itself on any path (even through mutual recursion), you need to specify the return type + |of $cycleSym or of a definition it's mutually recursive with. + |""" } class RecursiveValueNeedsResultType(cycleSym: Symbol)(using Context) extends CyclicMsg(RecursiveValueNeedsResultTypeID) { - def msg = em"""Recursive $cycleSym needs type""" + def msg = e"""Recursive $cycleSym needs type""" def explain = - em"""The definition of $cycleSym is recursive and you need to specify its type. - |""".stripMargin + e"""The definition of $cycleSym is recursive and you need to specify its type. + |""" } class CyclicReferenceInvolving(denot: SymDenotation)(using Context) extends CyclicMsg(CyclicReferenceInvolvingID) { def msg = val where = if denot.exists then s" involving $denot" else "" - em"Cyclic reference$where" + e"Cyclic reference$where" def explain = - em"""|$denot is declared as part of a cycle which makes it impossible for the - |compiler to decide upon ${denot.name}'s type. - |To avoid this error, try giving ${denot.name} an explicit type. - |""".stripMargin + e"""|$denot is declared as part of a cycle which makes it impossible for the + |compiler to decide upon ${denot.name}'s type. + |To avoid this error, try giving ${denot.name} an explicit type. + |""" } class CyclicReferenceInvolvingImplicit(cycleSym: Symbol)(using Context) extends CyclicMsg(CyclicReferenceInvolvingImplicitID) { - def msg = em"""Cyclic reference involving implicit $cycleSym""" + def msg = e"""Cyclic reference involving implicit $cycleSym""" def explain = - em"""|$cycleSym is declared as part of a cycle which makes it impossible for the - |compiler to decide upon ${cycleSym.name}'s type. - |This might happen when the right hand-side of $cycleSym's definition involves an implicit search. - |To avoid this error, try giving ${cycleSym.name} an explicit type. - |""".stripMargin + e"""|$cycleSym is declared as part of a cycle which makes it impossible for the + |compiler to decide upon ${cycleSym.name}'s type. + |This might happen when the right hand-side of $cycleSym's definition involves an implicit search. + |To avoid this error, try giving ${cycleSym.name} an explicit type. + |""" } class SkolemInInferred(tree: tpd.Tree, pt: Type, argument: tpd.Tree)(using Context) @@ -1253,39 +1252,39 @@ import cc.CaptureSet.IdentityCaptRefMap if argument.isEmpty then "" else i" from argument of type ${argument.tpe.widen}" def msg = - em"""Failure to generate given instance for type $pt$argStr) - | - |I found: $tree - |But the part corresponding to `` is not a reference that can be generated. - |This might be because resolution yielded as given instance a function that is not - |known to be total and side-effect free.""" + e"""Failure to generate given instance for type $pt$argStr) + | + |I found: $tree + |But the part corresponding to `` is not a reference that can be generated. + |This might be because resolution yielded as given instance a function that is not + |known to be total and side-effect free.""" def explain = - em"""The part of given resolution that corresponds to `` produced a term that - |is not a stable reference. Therefore a given instance could not be generated. - | - |To trouble-shoot the problem, try to supply an explicit expression instead of - |relying on implicit search at this point.""" + e"""The part of given resolution that corresponds to `` produced a term that + |is not a stable reference. Therefore a given instance could not be generated. + | + |To trouble-shoot the problem, try to supply an explicit expression instead of + |relying on implicit search at this point.""" class SuperQualMustBeParent(qual: untpd.Ident, cls: ClassSymbol)(using Context) extends ReferenceMsg(SuperQualMustBeParentID) { - def msg = em"""|$qual does not name a parent of $cls""" + def msg = e"""|$qual does not name a parent of $cls""" def explain = val parents: Seq[String] = (cls.info.parents map (_.typeSymbol.name.show)).sorted - em"""|When a qualifier ${hl("T")} is used in a ${hl("super")} prefix of the form ${hl("C.super[T]")}, - |${hl("T")} must be a parent type of ${hl("C")}. - | - |In this case, the parents of $cls are: - |${parents.mkString(" - ", "\n - ", "")} - |""".stripMargin + e"""|When a qualifier ${hl("T")} is used in a ${hl("super")} prefix of the form ${hl("C.super[T]")}, + |${hl("T")} must be a parent type of ${hl("C")}. + | + |In this case, the parents of $cls are: + |${parents.mkString(" - ", "\n - ", "")} + |""" } class VarArgsParamMustComeLast()(using Context) extends SyntaxMsg(VarArgsParamMustComeLastID) { - def msg = em"""${hl("varargs")} parameter must come last""" + def msg = e"""${hl("varargs")} parameter must come last""" def explain = - em"""|The ${hl("varargs")} field must be the last field in the method signature. - |Attempting to define a field in a method signature after a ${hl("varargs")} field is an error. - |""" + e"""|The ${hl("varargs")} field must be the last field in the method signature. + |Attempting to define a field in a method signature after a ${hl("varargs")} field is an error. + |""" } import typer.Typer.BindingPrec @@ -1307,28 +1306,28 @@ import cc.CaptureSet.IdentityCaptRefMap case BindingPrec.NothingBound => assert(false) } if (prec.isImportPrec) { - ex"""$howVisible$qualifier by ${em"${whereFound.importInfo}"}""" + ex"""$howVisible$qualifier by ${e"${whereFound.importInfo}"}""" } else - ex"""$howVisible$qualifier in ${em"${whereFound.owner}"}""" + ex"""$howVisible$qualifier in ${e"${whereFound.owner}"}""" } def msg = - i"""|Reference to ${em"$name"} is ambiguous, + i"""|Reference to ${e"$name"} is ambiguous, |it is both ${bindingString(newPrec, ctx)} |and ${bindingString(prevPrec, prevCtx, " subsequently")}""" def explain = - em"""|The compiler can't decide which of the possible choices you - |are referencing with $name: A definition of lower precedence - |in an inner scope, or a definition with higher precedence in - |an outer scope. - |Note: - | - Definitions in an enclosing scope take precedence over inherited definitions - | - Definitions take precedence over imports - | - Named imports take precedence over wildcard imports - | - You may replace a name when imported using - | ${hl("import")} scala.{ $name => ${name.show + "Tick"} } - |""" + e"""|The compiler can't decide which of the possible choices you + |are referencing with $name: A definition of lower precedence + |in an inner scope, or a definition with higher precedence in + |an outer scope. + |Note: + | - Definitions in an enclosing scope take precedence over inherited definitions + | - Definitions take precedence over imports + | - Named imports take precedence over wildcard imports + | - You may replace a name when imported using + | ${hl("import")} scala.{ $name => ${name.show + "Tick"} } + |""" } class MethodDoesNotTakeParameters(tree: tpd.Tree)(using Context) @@ -1347,7 +1346,7 @@ import cc.CaptureSet.IdentityCaptRefMap val more = if (tree.isInstanceOf[tpd.Apply]) " more" else "" val meth = methodSymbol val methStr = if (meth.exists) meth.showLocated else "expression" - em"$methStr does not take$more parameters" + e"$methStr does not take$more parameters" } def explain = { @@ -1366,28 +1365,28 @@ import cc.CaptureSet.IdentityCaptRefMap extends ReferenceMsg(AmbiguousOverloadID) { private def all = if (alternatives.length == 2) "both" else "all" def msg = - em"""|Ambiguous overload. The ${err.overloadedAltsStr(alternatives)} - |$all match ${err.expectedTypeStr(pt)}$addendum""".stripMargin - def explain = - em"""|There are ${alternatives.length} methods that could be referenced as the compiler knows too little - |about the expected type. - |You may specify the expected type e.g. by - |- assigning it to a value with a specified type, or - |- adding a type ascription as in ${hl("instance.myMethod: String => Int")} - |""" + e"""|Ambiguous overload. The ${err.overloadedAltsStr(alternatives)} + |$all match ${err.expectedTypeStr(pt)}$addendum""" + def explain = + e"""|There are ${alternatives.length} methods that could be referenced as the compiler knows too little + |about the expected type. + |You may specify the expected type e.g. by + |- assigning it to a value with a specified type, or + |- adding a type ascription as in ${hl("instance.myMethod: String => Int")} + |""" } class ReassignmentToVal(name: Name)(using Context) extends TypeMsg(ReassignmentToValID) { - def msg = em"""Reassignment to val $name""" - def explain = - em"""|You can not assign a new value to $name as values can't be changed. - |Keep in mind that every statement has a value, so you may e.g. use - | ${hl("val")} $name ${hl("= if (condition) 2 else 5")} - |In case you need a reassignable name, you can declare it as - |variable - | ${hl("var")} $name ${hl("=")} ... - |""".stripMargin + def msg = e"""Reassignment to val $name""" + def explain = + e"""|You can not assign a new value to $name as values can't be changed. + |Keep in mind that every statement has a value, so you may e.g. use + | ${hl("val")} $name ${hl("= if (condition) 2 else 5")} + |In case you need a reassignable name, you can declare it as + |variable + | ${hl("var")} $name ${hl("=")} ... + |""" } class TypeDoesNotTakeParameters(tpe: Type, params: List[untpd.Tree])(using Context) @@ -1396,35 +1395,35 @@ import cc.CaptureSet.IdentityCaptRefMap if tpe.typeSymbol.isAllOf(Provisional | TypeParam) then "\n(Note that F-bounds of type parameters may not be type lambdas)" else "" - def msg = em"$tpe does not take type parameters$fboundsAddendum" + def msg = e"$tpe does not take type parameters$fboundsAddendum" def explain = val ps = if (params.size == 1) s"a type parameter ${params.head}" else s"type parameters ${params.map(_.show).mkString(", ")}" - i"""You specified ${NoColor(ps)} for ${em"$tpe"}, which is not + i"""You specified ${NoColor(ps)} for ${e"$tpe"}, which is not |declared to take any. |""" } class VarValParametersMayNotBeCallByName(name: TermName, mutable: Boolean)(using Context) extends SyntaxMsg(VarValParametersMayNotBeCallByNameID) { - def varOrVal = if (mutable) em"${hl("var")}" else em"${hl("val")}" + def varOrVal = if (mutable) e"${hl("var")}" else e"${hl("val")}" def msg = s"$varOrVal parameters may not be call-by-name" def explain = - em"""${hl("var")} and ${hl("val")} parameters of classes and traits may no be call-by-name. In case you - |want the parameter to be evaluated on demand, consider making it just a parameter - |and a ${hl("def")} in the class such as - | ${s"class MyClass(${name}Tick: => String) {"} - | ${s" def $name() = ${name}Tick"} - | ${hl("}")} - |""" + e"""${hl("var")} and ${hl("val")} parameters of classes and traits may no be call-by-name. In case you + |want the parameter to be evaluated on demand, consider making it just a parameter + |and a ${hl("def")} in the class such as + | ${s"class MyClass(${name}Tick: => String) {"} + | ${s" def $name() = ${name}Tick"} + | ${hl("}")} + |""" } class MissingTypeParameterFor(tpe: Type)(using Context) extends SyntaxMsg(MissingTypeParameterForID) { def msg = - if (tpe.derivesFrom(defn.AnyKindClass)) em"${tpe} cannot be used as a value type" - else em"Missing type parameter for ${tpe}" + if (tpe.derivesFrom(defn.AnyKindClass)) e"${tpe} cannot be used as a value type" + else e"Missing type parameter for ${tpe}" def explain = "" } @@ -1432,8 +1431,8 @@ import cc.CaptureSet.IdentityCaptRefMap extends TypeMsg(MissingTypeParameterInTypeAppID) { def numParams = tpe.typeParams.length def parameters = if (numParams == 1) "parameter" else "parameters" - def msg = em"Missing type $parameters for $tpe" - def explain = em"A fully applied type is expected but $tpe takes $numParams $parameters" + def msg = e"Missing type $parameters for $tpe" + def explain = e"A fully applied type is expected but $tpe takes $numParams $parameters" } class MissingArgument(pname: Name, methString: String)(using Context) @@ -1453,42 +1452,42 @@ import cc.CaptureSet.IdentityCaptRefMap override def canExplain = !isBounds def msg = if isBounds then - em"Type argument ${tpe} does not overlap with $which bound $bound" + e"Type argument ${tpe} does not overlap with $which bound $bound" else - em"Type argument ${tpe} does not conform to $which bound $bound" + e"Type argument ${tpe} does not conform to $which bound $bound" class DoesNotConformToSelfType(category: String, selfType: Type, cls: Symbol, otherSelf: Type, relation: String, other: Symbol)( implicit ctx: Context) extends TypeMismatchMsg(selfType, otherSelf)(DoesNotConformToSelfTypeID) { - def msg = em"""$category: self type $selfType of $cls does not conform to self type $otherSelf - |of $relation $other""" + def msg = e"""$category: self type $selfType of $cls does not conform to self type $otherSelf + |of $relation $other""" } class DoesNotConformToSelfTypeCantBeInstantiated(tp: Type, selfType: Type)( implicit ctx: Context) extends TypeMismatchMsg(tp, selfType)(DoesNotConformToSelfTypeCantBeInstantiatedID) { - def msg = em"""$tp does not conform to its self type $selfType; cannot be instantiated""" + def msg = e"""$tp does not conform to its self type $selfType; cannot be instantiated""" } class IllegalParameterInit(found: Type, expected: Type, param: Symbol, cls: Symbol)(using Context) extends TypeMismatchMsg(found, expected)(IllegalParameterInitID): def msg = - em"""illegal parameter initialization of $param. - | - | The argument passed for $param has type: $found - | but $cls expects $param to have type: $expected""" + e"""illegal parameter initialization of $param. + | + | The argument passed for $param has type: $found + | but $cls expects $param to have type: $expected""" class AbstractMemberMayNotHaveModifier(sym: Symbol, flag: FlagSet)( implicit ctx: Context) extends SyntaxMsg(AbstractMemberMayNotHaveModifierID) { - def msg = em"""${hl("abstract")} $sym may not have `${flag.flagsString}` modifier""" + def msg = e"""${hl("abstract")} $sym may not have `${flag.flagsString}` modifier""" def explain = "" } class TypesAndTraitsCantBeImplicit()(using Context) extends SyntaxMsg(TypesAndTraitsCantBeImplicitID) { - def msg = em"""${hl("implicit")} modifier cannot be used for types or traits""" + def msg = e"""${hl("implicit")} modifier cannot be used for types or traits""" def explain = "" } @@ -1496,20 +1495,20 @@ import cc.CaptureSet.IdentityCaptRefMap implicit ctx: Context) extends SyntaxMsg(OnlyClassesCanBeAbstractID) { def explain = "" - def msg = em"""${hl("abstract")} modifier can be used only for classes; it should be omitted for abstract members""" + def msg = e"""${hl("abstract")} modifier can be used only for classes; it should be omitted for abstract members""" } class AbstractOverrideOnlyInTraits(sym: Symbol)( implicit ctx: Context) extends SyntaxMsg(AbstractOverrideOnlyInTraitsID) { - def msg = em"""${hl("abstract override")} modifier only allowed for members of traits""" + def msg = e"""${hl("abstract override")} modifier only allowed for members of traits""" def explain = "" } class TraitsMayNotBeFinal(sym: Symbol)( implicit ctx: Context) extends SyntaxMsg(TraitsMayNotBeFinalID) { - def msg = em"""$sym may not be ${hl("final")}""" + def msg = e"""$sym may not be ${hl("final")}""" def explain = "A trait can never be final since it is abstract and must be extended to be useful." } @@ -1517,14 +1516,14 @@ import cc.CaptureSet.IdentityCaptRefMap class NativeMembersMayNotHaveImplementation(sym: Symbol)( implicit ctx: Context) extends SyntaxMsg(NativeMembersMayNotHaveImplementationID) { - def msg = em"""${hl("@native")} members may not have an implementation""" + def msg = e"""${hl("@native")} members may not have an implementation""" def explain = "" } class TraitMayNotDefineNativeMethod(sym: Symbol)( implicit ctx: Context) extends SyntaxMsg(TraitMayNotDefineNativeMethodID) { - def msg = em"""A trait cannot define a ${hl("@native")} method.""" + def msg = e"""A trait cannot define a ${hl("@native")} method.""" def explain = "" } @@ -1535,35 +1534,35 @@ import cc.CaptureSet.IdentityCaptRefMap private def varNote = if (sym.is(Mutable)) "Note that variables need to be initialized to be defined." else "" - def msg = em"""Declaration of $sym not allowed here: only classes can have declared but undefined members""" + def msg = e"""Declaration of $sym not allowed here: only classes can have declared but undefined members""" def explain = s"$varNote" } class CannotExtendAnyVal(sym: Symbol)(using Context) extends SyntaxMsg(CannotExtendAnyValID) { - def msg = em"""$sym cannot extend ${hl("AnyVal")}""" + def msg = e"""$sym cannot extend ${hl("AnyVal")}""" def explain = - em"""Only classes (not traits) are allowed to extend ${hl("AnyVal")}, but traits may extend - |${hl("Any")} to become ${Green("\"universal traits\"")} which may only have ${hl("def")} members. - |Universal traits can be mixed into classes that extend ${hl("AnyVal")}. - |""" + e"""Only classes (not traits) are allowed to extend ${hl("AnyVal")}, but traits may extend + |${hl("Any")} to become ${Green("\"universal traits\"")} which may only have ${hl("def")} members. + |Universal traits can be mixed into classes that extend ${hl("AnyVal")}. + |""" } class CannotExtendJavaEnum(sym: Symbol)(using Context) extends SyntaxMsg(CannotExtendJavaEnumID) { - def msg = em"""$sym cannot extend ${hl("java.lang.Enum")}: only enums defined with the ${hl("enum")} syntax can""" + def msg = e"""$sym cannot extend ${hl("java.lang.Enum")}: only enums defined with the ${hl("enum")} syntax can""" def explain = "" } class CannotExtendContextFunction(sym: Symbol)(using Context) extends SyntaxMsg(CannotExtendFunctionID) { - def msg = em"""$sym cannot extend a context function class""" + def msg = e"""$sym cannot extend a context function class""" 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 msg = e"""not enough arguments for constructor Enum: ${hl("(name: String, ordinal: Int)")}: ${hl(parent.show)}""" def explain = "" } @@ -1578,7 +1577,7 @@ import cc.CaptureSet.IdentityCaptRefMap |""".stripMargin } - def msg = em"""$sym cannot have the same name as ${cls.showLocated} -- """ + reasonMessage + def msg = e"""$sym cannot have the same name as ${cls.showLocated} -- """ + reasonMessage def explain = "" } object CannotHaveSameNameAs { @@ -1589,31 +1588,31 @@ import cc.CaptureSet.IdentityCaptRefMap class ValueClassesMayNotDefineInner(valueClass: Symbol, inner: Symbol)(using Context) extends SyntaxMsg(ValueClassesMayNotDefineInnerID) { - def msg = em"""Value classes may not define an inner class""" + def msg = e"""Value classes may not define an inner class""" def explain = "" } class ValueClassesMayNotDefineNonParameterField(valueClass: Symbol, field: Symbol)(using Context) extends SyntaxMsg(ValueClassesMayNotDefineNonParameterFieldID) { - def msg = em"""Value classes may not define non-parameter field""" + def msg = e"""Value classes may not define non-parameter field""" def explain = "" } class ValueClassesMayNotDefineASecondaryConstructor(valueClass: Symbol, constructor: Symbol)(using Context) extends SyntaxMsg(ValueClassesMayNotDefineASecondaryConstructorID) { - def msg = em"""Value classes may not define a secondary constructor""" + def msg = e"""Value classes may not define a secondary constructor""" def explain = "" } class ValueClassesMayNotContainInitalization(valueClass: Symbol)(using Context) extends SyntaxMsg(ValueClassesMayNotContainInitalizationID) { - def msg = em"""Value classes may not contain initialization statements""" + def msg = e"""Value classes may not contain initialization statements""" def explain = "" } class ValueClassesMayNotBeAbstract(valueClass: Symbol)(using Context) extends SyntaxMsg(ValueClassesMayNotBeAbstractID) { - def msg = em"""Value classes may not be ${hl("abstract")}""" + def msg = e"""Value classes may not be ${hl("abstract")}""" def explain = "" } @@ -1632,14 +1631,14 @@ import cc.CaptureSet.IdentityCaptRefMap class ValueClassParameterMayNotBeAVar(valueClass: Symbol, param: Symbol)(using Context) extends SyntaxMsg(ValueClassParameterMayNotBeAVarID) { - def msg = em"""A value class parameter may not be a ${hl("var")}""" + def msg = e"""A value class parameter may not be a ${hl("var")}""" def explain = - em"""A value class must have exactly one ${hl("val")} parameter.""" + e"""A value class must have exactly one ${hl("val")} parameter.""" } class ValueClassNeedsOneValParam(valueClass: Symbol)(using Context) extends SyntaxMsg(ValueClassNeedsExactlyOneValParamID) { - def msg = em"""Value class needs one ${hl("val")} parameter""" + def msg = e"""Value class needs one ${hl("val")} parameter""" def explain = "" } @@ -1651,12 +1650,12 @@ import cc.CaptureSet.IdentityCaptRefMap class SuperCallsNotAllowedInlineable(symbol: Symbol)(using Context) extends SyntaxMsg(SuperCallsNotAllowedInlineableID) { - def msg = em"Super call not allowed in inlineable $symbol" + def msg = e"Super call not allowed in inlineable $symbol" def explain = "Method inlining prohibits calling superclass methods, as it may lead to confusion about which super is being called." } class NotAPath(tp: Type, usage: String)(using Context) extends TypeMsg(NotAPathID): - def msg = em"$tp is not a valid $usage, since it is not an immutable path" + def msg = e"$tp is not a valid $usage, since it is not an immutable path" def explain = i"""An immutable path is | - a reference to an immutable value, or @@ -1673,78 +1672,78 @@ import cc.CaptureSet.IdentityCaptRefMap extends SyntaxMsg(DuplicatePrivateProtectedQualifierID) { def msg = "Duplicate private/protected qualifier" def explain = - em"It is not allowed to combine `private` and `protected` modifiers even if they are qualified to different scopes" + e"It is not allowed to combine `private` and `protected` modifiers even if they are qualified to different scopes" } class ExpectedStartOfTopLevelDefinition()(using Context) extends SyntaxMsg(ExpectedStartOfTopLevelDefinitionID) { def msg = "Expected start of definition" def explain = - em"You have to provide either ${hl("class")}, ${hl("trait")}, ${hl("object")}, or ${hl("enum")} definitions after qualifiers" + e"You have to provide either ${hl("class")}, ${hl("trait")}, ${hl("object")}, or ${hl("enum")} definitions after qualifiers" } class NoReturnFromInlineable(owner: Symbol)(using Context) extends SyntaxMsg(NoReturnFromInlineableID) { - def msg = em"No explicit ${hl("return")} allowed from inlineable $owner" + def msg = e"No explicit ${hl("return")} allowed from inlineable $owner" def explain = - em"""Methods marked with ${hl("inline")} modifier may not use ${hl("return")} statements. - |Instead, you should rely on the last expression's value being - |returned from a method. - |""" + e"""Methods marked with ${hl("inline")} modifier may not use ${hl("return")} statements. + |Instead, you should rely on the last expression's value being + |returned from a method. + |""" } class ReturnOutsideMethodDefinition(owner: Symbol)(using Context) extends SyntaxMsg(ReturnOutsideMethodDefinitionID) { - def msg = em"${hl("return")} outside method definition" + def msg = e"${hl("return")} outside method definition" def explain = - em"""You used ${hl("return")} in ${owner}. - |${hl("return")} is a keyword and may only be used within method declarations. - |""" + e"""You used ${hl("return")} in ${owner}. + |${hl("return")} is a keyword and may only be used within method declarations. + |""" } class ExtendFinalClass(clazz:Symbol, finalClazz: Symbol)(using Context) extends SyntaxMsg(ExtendFinalClassID) { - def msg = em"$clazz cannot extend ${hl("final")} $finalClazz" + def msg = e"$clazz cannot extend ${hl("final")} $finalClazz" def explain = - em"""A class marked with the ${hl("final")} keyword cannot be extended""" + e"""A class marked with the ${hl("final")} keyword cannot be extended""" } class ExpectedTypeBoundOrEquals(found: Token)(using Context) extends SyntaxMsg(ExpectedTypeBoundOrEqualsID) { - def msg = em"${hl("=")}, ${hl(">:")}, or ${hl("<:")} expected, but ${Tokens.showToken(found)} found" + def msg = e"${hl("=")}, ${hl(">:")}, or ${hl("<:")} expected, but ${Tokens.showToken(found)} found" def explain = - em"""Type parameters and abstract types may be constrained by a type bound. - |Such type bounds limit the concrete values of the type variables and possibly - |reveal more information about the members of such types. - | - |A lower type bound ${hl("B >: A")} expresses that the type variable ${hl("B")} - |refers to a supertype of type ${hl("A")}. - | - |An upper type bound ${hl("T <: A")} declares that type variable ${hl("T")} - |refers to a subtype of type ${hl("A")}. - |""" + e"""Type parameters and abstract types may be constrained by a type bound. + |Such type bounds limit the concrete values of the type variables and possibly + |reveal more information about the members of such types. + | + |A lower type bound ${hl("B >: A")} expresses that the type variable ${hl("B")} + |refers to a supertype of type ${hl("A")}. + | + |An upper type bound ${hl("T <: A")} declares that type variable ${hl("T")} + |refers to a subtype of type ${hl("A")}. + |""" } class ClassAndCompanionNameClash(cls: Symbol, other: Symbol)(using Context) extends NamingMsg(ClassAndCompanionNameClashID) { def msg = val name = cls.name.stripModuleClassSuffix - em"Name clash: both ${cls.owner} and its companion object defines $name" + e"Name clash: both ${cls.owner} and its companion object defines $name" def explain = - em"""|A ${cls.kindString} and its companion object cannot both define a ${hl("class")}, ${hl("trait")} or ${hl("object")} with the same name: - | - ${cls.owner} defines ${cls} - | - ${other.owner} defines ${other}""" + e"""|A ${cls.kindString} and its companion object cannot both define a ${hl("class")}, ${hl("trait")} or ${hl("object")} with the same name: + | - ${cls.owner} defines ${cls} + | - ${other.owner} defines ${other}""" } class TailrecNotApplicable(symbol: Symbol)(using Context) extends SyntaxMsg(TailrecNotApplicableID) { def msg = { val reason = - if (!symbol.is(Method)) em"$symbol isn't a method" - else if (symbol.is(Deferred)) em"$symbol is abstract" - else if (!symbol.isEffectivelyFinal) em"$symbol is neither ${hl("private")} nor ${hl("final")} so can be overridden" - else em"$symbol contains no recursive calls" + if (!symbol.is(Method)) e"$symbol isn't a method" + else if (symbol.is(Deferred)) e"$symbol is abstract" + else if (!symbol.isEffectivelyFinal) e"$symbol is neither ${hl("private")} nor ${hl("final")} so can be overridden" + else e"$symbol contains no recursive calls" s"TailRec optimisation not applicable, $reason" } @@ -1756,54 +1755,54 @@ import cc.CaptureSet.IdentityCaptRefMap def kind = MessageKind.Compatibility def msg = val originalType = ctx.printer.dclsText(boundSyms, "; ").show - em"""An existential type that came from a Scala-2 classfile for $classRoot - |cannot be mapped accurately to a Scala-3 equivalent. - |original type : $tp forSome ${originalType} - |reduces to : $tp1 - |type used instead: $tp2 - |This choice can cause follow-on type errors or hide type errors. - |Proceed at own risk.""" + e"""An existential type that came from a Scala-2 classfile for $classRoot + |cannot be mapped accurately to a Scala-3 equivalent. + |original type : $tp forSome ${originalType} + |reduces to : $tp1 + |type used instead: $tp2 + |This choice can cause follow-on type errors or hide type errors. + |Proceed at own risk.""" def explain = - em"""Existential types in their full generality are no longer supported. - |Scala-3 does applications of class types to wildcard type arguments. - |Other forms of existential types that come from Scala-2 classfiles - |are only approximated in a best-effort way.""" + e"""Existential types in their full generality are no longer supported. + |Scala-3 does applications of class types to wildcard type arguments. + |Other forms of existential types that come from Scala-2 classfiles + |are only approximated in a best-effort way.""" } class OnlyFunctionsCanBeFollowedByUnderscore(tp: Type)(using Context) extends SyntaxMsg(OnlyFunctionsCanBeFollowedByUnderscoreID) { - def msg = em"Only function types can be followed by ${hl("_")} but the current expression has type $tp" + def msg = e"Only function types can be followed by ${hl("_")} but the current expression has type $tp" def explain = - em"""The syntax ${hl("x _")} is no longer supported if ${hl("x")} is not a function. - |To convert to a function value, you need to explicitly write ${hl("() => x")}""" + e"""The syntax ${hl("x _")} is no longer supported if ${hl("x")} is not a function. + |To convert to a function value, you need to explicitly write ${hl("() => x")}""" } class MissingEmptyArgumentList(method: String)(using Context) extends SyntaxMsg(MissingEmptyArgumentListID) { - def msg = em"$method must be called with ${hl("()")} argument" + def msg = e"$method must be called with ${hl("()")} argument" def explain = { val codeExample = """def next(): T = ... |next // is expanded to next()""" - em"""Previously an empty argument list () was implicitly inserted when calling a nullary method without arguments. E.g. - | - |$codeExample - | - |In Dotty, this idiom is an error. The application syntax has to follow exactly the parameter syntax. - |Excluded from this rule are methods that are defined in Java or that override methods defined in Java.""" + e"""Previously an empty argument list () was implicitly inserted when calling a nullary method without arguments. E.g. + | + |$codeExample + | + |In Dotty, this idiom is an error. The application syntax has to follow exactly the parameter syntax. + |Excluded from this rule are methods that are defined in Java or that override methods defined in Java.""" } } class DuplicateNamedTypeParameter(name: Name)(using Context) extends SyntaxMsg(DuplicateNamedTypeParameterID) { - def msg = em"Type parameter $name was defined multiple times." + def msg = e"Type parameter $name was defined multiple times." def explain = "" } class UndefinedNamedTypeParameter(undefinedName: Name, definedNames: List[Name])(using Context) extends SyntaxMsg(UndefinedNamedTypeParameterID) { - def msg = em"Type parameter $undefinedName is undefined. Expected one of ${definedNames.map(_.show).mkString(", ")}." + def msg = e"Type parameter $undefinedName is undefined. Expected one of ${definedNames.map(_.show).mkString(", ")}." def explain = "" } @@ -1820,7 +1819,7 @@ import cc.CaptureSet.IdentityCaptRefMap } class TraitIsExpected(symbol: Symbol)(using Context) extends SyntaxMsg(TraitIsExpectedID) { - def msg = em"$symbol is not a trait" + def msg = e"$symbol is not a trait" def explain = { val errorCodeExample = """class A @@ -1833,21 +1832,21 @@ import cc.CaptureSet.IdentityCaptRefMap | |val a = new A with B // compiles normally""".stripMargin - em"""Only traits can be mixed into classes using a ${hl("with")} keyword. - |Consider the following example: - | - |$errorCodeExample - | - |The example mentioned above would fail because B is not a trait. - |But if you make B a trait it will be compiled without any errors: - | - |$codeExample - |""" + e"""Only traits can be mixed into classes using a ${hl("with")} keyword. + |Consider the following example: + | + |$errorCodeExample + | + |The example mentioned above would fail because B is not a trait. + |But if you make B a trait it will be compiled without any errors: + | + |$codeExample + |""" } } class TraitRedefinedFinalMethodFromAnyRef(method: Symbol)(using Context) extends SyntaxMsg(TraitRedefinedFinalMethodFromAnyRefID) { - def msg = em"Traits cannot redefine final $method from ${hl("class AnyRef")}." + def msg = e"Traits cannot redefine final $method from ${hl("class AnyRef")}." def explain = "" } @@ -1863,32 +1862,32 @@ import cc.CaptureSet.IdentityCaptRefMap else "" def msg = if conflicting.isTerm != name.isTermName then - em"$name clashes with $conflicting$where; the two must be defined together" + e"$name clashes with $conflicting$where; the two must be defined together" else - em"$name is already defined as $conflicting$where$note" + e"$name is already defined as $conflicting$where$note" def explain = "" class PackageNameAlreadyDefined(pkg: Symbol)(using Context) extends NamingMsg(PackageNameAlreadyDefinedID) { lazy val (where, or) = if pkg.associatedFile == null then ("", "") else (s" in ${pkg.associatedFile}", " or delete the containing class file") - def msg = em"""${pkg.name} is the name of $pkg$where. + def msg = e"""${pkg.name} is the name of $pkg$where. |It cannot be used at the same time as the name of a package.""" def explain = - em"""An ${hl("object")} or other toplevel definition cannot have the same name as an existing ${hl("package")}. - |Rename either one of them$or.""" + e"""An ${hl("object")} or other toplevel definition cannot have the same name as an existing ${hl("package")}. + |Rename either one of them$or.""" } class UnapplyInvalidNumberOfArguments(qual: untpd.Tree, argTypes: List[Type])(using Context) extends SyntaxMsg(UnapplyInvalidNumberOfArgumentsID) { - def msg = em"Wrong number of argument patterns for $qual; expected: ($argTypes%, %)" + def msg = e"Wrong number of argument patterns for $qual; expected: ($argTypes%, %)" def explain = - em"""The Unapply method of $qual was used with incorrect number of arguments. - |Expected usage would be something like: - |case $qual(${argTypes.map(_ => '_')}%, %) => ... - | - |where subsequent arguments would have following types: ($argTypes%, %). - |""".stripMargin + e"""The Unapply method of $qual was used with incorrect number of arguments. + |Expected usage would be something like: + |case $qual(${argTypes.map(_ => '_')}%, %) => ... + | + |where subsequent arguments would have following types: ($argTypes%, %). + |""" } class UnapplyInvalidReturnType(unapplyResult: Type, unapplyName: Name)(using Context) @@ -1898,62 +1897,62 @@ import cc.CaptureSet.IdentityCaptRefMap if Feature.migrateTo3 && unapplyName == nme.unapplySeq then "\nYou might want to try to rewrite the extractor to use `unapply` instead." else "" - em"""| ${Red(i"$unapplyResult")} is not a valid result type of an $unapplyName method of an ${Magenta("extractor")}.$addendum""" + e"""| ${Red(i"$unapplyResult")} is not a valid result type of an $unapplyName method of an ${Magenta("extractor")}.$addendum""" def explain = if (unapplyName.show == "unapply") - em""" - |To be used as an extractor, an unapply method has to return a type that either: - | - has members ${Magenta("isEmpty: Boolean")} and ${Magenta("get: S")} (usually an ${Green("Option[S]")}) - | - is a ${Green("Boolean")} - | - is a ${Green("Product")} (like a ${Magenta("Tuple2[T1, T2]")}) - | - |class A(val i: Int) - | - |object B { - | def unapply(a: A): ${Green("Option[Int]")} = Some(a.i) - |} - | - |object C { - | def unapply(a: A): ${Green("Boolean")} = a.i == 2 - |} - | - |object D { - | def unapply(a: A): ${Green("(Int, Int)")} = (a.i, a.i) - |} - | - |object Test { - | def test(a: A) = a match { - | ${Magenta("case B(1)")} => 1 - | ${Magenta("case a @ C()")} => 2 - | ${Magenta("case D(3, 3)")} => 3 - | } - |} - """.stripMargin + e""" + |To be used as an extractor, an unapply method has to return a type that either: + | - has members ${Magenta("isEmpty: Boolean")} and ${Magenta("get: S")} (usually an ${Green("Option[S]")}) + | - is a ${Green("Boolean")} + | - is a ${Green("Product")} (like a ${Magenta("Tuple2[T1, T2]")}) + | + |class A(val i: Int) + | + |object B { + | def unapply(a: A): ${Green("Option[Int]")} = Some(a.i) + |} + | + |object C { + | def unapply(a: A): ${Green("Boolean")} = a.i == 2 + |} + | + |object D { + | def unapply(a: A): ${Green("(Int, Int)")} = (a.i, a.i) + |} + | + |object Test { + | def test(a: A) = a match { + | ${Magenta("case B(1)")} => 1 + | ${Magenta("case a @ C()")} => 2 + | ${Magenta("case D(3, 3)")} => 3 + | } + |} + """ else - em""" - |To be used as an extractor, an unapplySeq method has to return a type which has members - |${Magenta("isEmpty: Boolean")} and ${Magenta("get: S")} where ${Magenta("S <: Seq[V]")} (usually an ${Green("Option[Seq[V]]")}): - | - |object CharList { - | def unapplySeq(s: String): ${Green("Option[Seq[Char]")} = Some(s.toList) - | - | "example" match { - | ${Magenta("case CharList(c1, c2, c3, c4, _, _, _)")} => - | println(s"$$c1,$$c2,$$c3,$$c4") - | case _ => - | println("Expected *exactly* 7 characters!") - | } - |} - """.stripMargin + e""" + |To be used as an extractor, an unapplySeq method has to return a type which has members + |${Magenta("isEmpty: Boolean")} and ${Magenta("get: S")} where ${Magenta("S <: Seq[V]")} (usually an ${Green("Option[Seq[V]]")}): + | + |object CharList { + | def unapplySeq(s: String): ${Green("Option[Seq[Char]")} = Some(s.toList) + | + | "example" match { + | ${Magenta("case CharList(c1, c2, c3, c4, _, _, _)")} => + | println(s"$$c1,$$c2,$$c3,$$c4") + | case _ => + | println("Expected *exactly* 7 characters!") + | } + |} + """ } class StaticFieldsOnlyAllowedInObjects(member: Symbol)(using Context) extends SyntaxMsg(StaticFieldsOnlyAllowedInObjectsID) { - def msg = em"${hl("@static")} $member in ${member.owner} must be defined inside a static ${hl("object")}." + def msg = e"${hl("@static")} $member in ${member.owner} must be defined inside a static ${hl("object")}." def explain = - em"${hl("@static")} members are only allowed inside objects." + e"${hl("@static")} members are only allowed inside objects." } class StaticFieldsShouldPrecedeNonStatic(member: Symbol, defns: List[tpd.Tree])(using Context) extends SyntaxMsg(StaticFieldsShouldPrecedeNonStaticID) { - def msg = em"${hl("@static")} $member in ${member.owner} must be defined before non-static fields." + def msg = e"${hl("@static")} $member in ${member.owner} must be defined before non-static fields." def explain = { val nonStatics = defns.takeWhile(_.symbol != member).take(3).filter(_.isInstanceOf[tpd.ValDef]) val codeExample = s"""object ${member.owner.name.firstPart} { @@ -1961,30 +1960,30 @@ import cc.CaptureSet.IdentityCaptRefMap | ${nonStatics.map(m => s"${m.symbol} = ...").mkString("\n ")} | ... |}""" - em"""The fields annotated with @static should precede any non @static fields. - |This ensures that we do not introduce surprises for users in initialization order of this class. - |Static field are initialized when class loading the code of Foo. - |Non static fields are only initialized the first time that Foo is accessed. - | - |The definition of ${member.name} should have been before the non ${hl("@static val")}s: - |$codeExample - |""" + e"""The fields annotated with @static should precede any non @static fields. + |This ensures that we do not introduce surprises for users in initialization order of this class. + |Static field are initialized when class loading the code of Foo. + |Non static fields are only initialized the first time that Foo is accessed. + | + |The definition of ${member.name} should have been before the non ${hl("@static val")}s: + |$codeExample + |""" } } class CyclicInheritance(symbol: Symbol, addendum: => String)(using Context) extends SyntaxMsg(CyclicInheritanceID) { - def msg = em"Cyclic inheritance: $symbol extends itself$addendum" + def msg = e"Cyclic inheritance: $symbol extends itself$addendum" def explain = { val codeExample = "class A extends A" - em"""Cyclic inheritance is prohibited in Dotty. - |Consider the following example: - | - |$codeExample - | - |The example mentioned above would fail because this type of inheritance hierarchy - |creates a "cycle" where a not yet defined class A extends itself which makes - |impossible to instantiate an object of this class""" + e"""Cyclic inheritance is prohibited in Dotty. + |Consider the following example: + | + |$codeExample + | + |The example mentioned above would fail because this type of inheritance hierarchy + |creates a "cycle" where a not yet defined class A extends itself which makes + |impossible to instantiate an object of this class""" } } @@ -1998,27 +1997,27 @@ import cc.CaptureSet.IdentityCaptRefMap if (file != null) (s" in $file", file.toString) else ("", "the signature") - em"""Bad symbolic reference. A signature$location - |refers to $denotationName in ${denotationOwner.showKind} ${denotationOwner.showFullName} which is not available. - |It may be completely missing from the current classpath, or the version on - |the classpath might be incompatible with the version used when compiling $src.""" + e"""Bad symbolic reference. A signature$location + |refers to $denotationName in ${denotationOwner.showKind} ${denotationOwner.showFullName} which is not available. + |It may be completely missing from the current classpath, or the version on + |the classpath might be incompatible with the version used when compiling $src.""" } def explain = "" } class UnableToExtendSealedClass(pclazz: Symbol)(using Context) extends SyntaxMsg(UnableToExtendSealedClassID) { - def msg = em"Cannot extend ${hl("sealed")} $pclazz in a different source file" + def msg = e"Cannot extend ${hl("sealed")} $pclazz in a different source file" def explain = "A sealed class or trait can only be extended in the same file as its declaration" } class SymbolHasUnparsableVersionNumber(symbol: Symbol, errorMessage: String)(using Context) extends SyntaxMsg(SymbolHasUnparsableVersionNumberID) { - def msg = em"${symbol.showLocated} has an unparsable version number: $errorMessage" + def msg = e"${symbol.showLocated} has an unparsable version number: $errorMessage" def explain = - em"""The ${symbol.showLocated} is marked with ${hl("@migration")} indicating it has changed semantics - |between versions and the ${hl("-Xmigration")} settings is used to warn about constructs - |whose behavior may have changed since version change.""" + e"""The ${symbol.showLocated} is marked with ${hl("@migration")} indicating it has changed semantics + |between versions and the ${hl("-Xmigration")} settings is used to warn about constructs + |whose behavior may have changed since version change.""" } class SymbolChangedSemanticsInVersion( @@ -2026,16 +2025,16 @@ import cc.CaptureSet.IdentityCaptRefMap migrationVersion: ScalaVersion, migrationMessage: String )(using Context) extends SyntaxMsg(SymbolChangedSemanticsInVersionID) { - def msg = em"${symbol.showLocated} has changed semantics in version $migrationVersion: $migrationMessage" + def msg = e"${symbol.showLocated} has changed semantics in version $migrationVersion: $migrationMessage" def explain = - em"""The ${symbol.showLocated} is marked with ${hl("@migration")} indicating it has changed semantics - |between versions and the ${hl("-Xmigration")} settings is used to warn about constructs - |whose behavior may have changed since version change.""" + e"""The ${symbol.showLocated} is marked with ${hl("@migration")} indicating it has changed semantics + |between versions and the ${hl("-Xmigration")} settings is used to warn about constructs + |whose behavior may have changed since version change.""" } class UnableToEmitSwitch()(using Context) extends SyntaxMsg(UnableToEmitSwitchID) { - def msg = em"Could not emit switch for ${hl("@switch")} annotated match" + def msg = e"Could not emit switch for ${hl("@switch")} annotated match" def explain = { val codeExample = """val ConstantB = 'B' @@ -2048,46 +2047,46 @@ import cc.CaptureSet.IdentityCaptRefMap | case _ => 5 |}""".stripMargin - em"""If annotated with ${hl("@switch")}, the compiler will verify that the match has been compiled to a - |tableswitch or lookupswitch and issue an error if it instead compiles into a series of conditional - |expressions. Example usage: - | - |$codeExample - | - |The compiler will not apply the optimisation if: - |- the matched value is not of type ${hl("Int")}, ${hl("Byte")}, ${hl("Short")} or ${hl("Char")} - |- the matched value is not a constant literal - |- there are less than three cases""" + e"""If annotated with ${hl("@switch")}, the compiler will verify that the match has been compiled to a + |tableswitch or lookupswitch and issue an error if it instead compiles into a series of conditional + |expressions. Example usage: + | + |$codeExample + | + |The compiler will not apply the optimisation if: + |- the matched value is not of type ${hl("Int")}, ${hl("Byte")}, ${hl("Short")} or ${hl("Char")} + |- the matched value is not a constant literal + |- there are less than three cases""" } } class MissingCompanionForStatic(member: Symbol)(using Context) extends SyntaxMsg(MissingCompanionForStaticID) { - def msg = em"${member.owner} does not have a companion class" + def msg = e"${member.owner} does not have a companion class" def explain = - em"An object that contains ${hl("@static")} members must have a companion class." + e"An object that contains ${hl("@static")} members must have a companion class." } class PolymorphicMethodMissingTypeInParent(rsym: Symbol, parentSym: Symbol)(using Context) extends SyntaxMsg(PolymorphicMethodMissingTypeInParentID) { - def msg = em"Polymorphic refinement $rsym without matching type in parent $parentSym is no longer allowed" + def msg = e"Polymorphic refinement $rsym without matching type in parent $parentSym is no longer allowed" def explain = - em"""Polymorphic $rsym is not allowed in the structural refinement of $parentSym because - |$rsym does not override any method in $parentSym. Structural refinement does not allow for - |polymorphic methods.""" + e"""Polymorphic $rsym is not allowed in the structural refinement of $parentSym because + |$rsym does not override any method in $parentSym. Structural refinement does not allow for + |polymorphic methods.""" } class ParamsNoInline(owner: Symbol)(using Context) extends SyntaxMsg(ParamsNoInlineID) { - def msg = em"""${hl("inline")} modifier can only be used for parameters of inline methods""" + def msg = e"""${hl("inline")} modifier can only be used for parameters of inline methods""" def explain = "" } class JavaSymbolIsNotAValue(symbol: Symbol)(using Context) extends TypeMsg(JavaSymbolIsNotAValueID) { def msg = { val kind = - if (symbol is Package) em"$symbol" - else em"Java defined ${hl("class " + symbol.name)}" + if (symbol is Package) e"$symbol" + else e"Java defined ${hl("class " + symbol.name)}" s"$kind is not a value" } @@ -2145,10 +2144,10 @@ import cc.CaptureSet.IdentityCaptRefMap "Name clash between inherited members" atPhase(typerPhase) { - em"""$clashDescription: - |${previousDecl.showDcl} ${symLocation(previousDecl)} and - |${decl.showDcl} ${symLocation(decl)} - |""" + e"""$clashDescription: + |${previousDecl.showDcl} ${symLocation(previousDecl)} and + |${decl.showDcl} ${symLocation(decl)} + |""" } + details } def explain = "" @@ -2168,34 +2167,34 @@ import cc.CaptureSet.IdentityCaptRefMap // Relative of CyclicReferenceInvolvingImplicit and RecursiveValueNeedsResultType class TermMemberNeedsResultTypeForImplicitSearch(cycleSym: Symbol)(using Context) extends CyclicMsg(TermMemberNeedsNeedsResultTypeForImplicitSearchID) { - def msg = em"""$cycleSym needs result type because its right-hand side attempts implicit search""" + def msg = e"""$cycleSym needs result type because its right-hand side attempts implicit search""" def explain = - em"""|The right hand-side of $cycleSym's definition requires an implicit search at the highlighted position. - |To avoid this error, give `$cycleSym` an explicit type. - |""".stripMargin + e"""|The right hand-side of $cycleSym's definition requires an implicit search at the highlighted position. + |To avoid this error, give `$cycleSym` an explicit type. + |""" } class ClassCannotExtendEnum(cls: Symbol, parent: Symbol)(using Context) extends SyntaxMsg(ClassCannotExtendEnumID) { - def msg = em"""$cls in ${cls.owner} extends enum ${parent.name}, but extending enums is prohibited.""" + def msg = e"""$cls in ${cls.owner} extends enum ${parent.name}, but extending enums is prohibited.""" def explain = "" } class NotAnExtractor(tree: untpd.Tree)(using Context) extends SyntaxMsg(NotAnExtractorID) { - def msg = em"$tree cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method" + def msg = e"$tree cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method" def explain = - em"""|An ${hl("unapply")} method should be defined in an ${hl("object")} as follow: - | - If it is just a test, return a ${hl("Boolean")}. For example ${hl("case even()")} - | - If it returns a single sub-value of type T, return an ${hl("Option[T]")} - | - If it returns several sub-values T1,...,Tn, group them in an optional tuple ${hl("Option[(T1,...,Tn)]")} - | - |Sometimes, the number of sub-values isn't fixed and we would like to return a sequence. - |For this reason, you can also define patterns through ${hl("unapplySeq")} which returns ${hl("Option[Seq[T]]")}. - |This mechanism is used for instance in pattern ${hl("case List(x1, ..., xn)")}""".stripMargin + e"""|An ${hl("unapply")} method should be defined in an ${hl("object")} as follow: + | - If it is just a test, return a ${hl("Boolean")}. For example ${hl("case even()")} + | - If it returns a single sub-value of type T, return an ${hl("Option[T]")} + | - If it returns several sub-values T1,...,Tn, group them in an optional tuple ${hl("Option[(T1,...,Tn)]")} + | + |Sometimes, the number of sub-values isn't fixed and we would like to return a sequence. + |For this reason, you can also define patterns through ${hl("unapplySeq")} which returns ${hl("Option[Seq[T]]")}. + |This mechanism is used for instance in pattern ${hl("case List(x1, ..., xn)")}""" } class MemberWithSameNameAsStatic()(using Context) extends SyntaxMsg(MemberWithSameNameAsStaticID) { - def msg = em"Companion classes cannot define members with same name as a ${hl("@static")} member" + def msg = e"Companion classes cannot define members with same name as a ${hl("@static")} member" def explain = "" } @@ -2204,25 +2203,25 @@ import cc.CaptureSet.IdentityCaptRefMap def kind = MessageKind.PotentialIssue def msg = "A pure expression does nothing in statement position; you may be omitting necessary parentheses" def explain = - em"""The pure expression $stat doesn't have any side effect and its result is not assigned elsewhere. - |It can be removed without changing the semantics of the program. This may indicate an error.""".stripMargin + e"""The pure expression $stat doesn't have any side effect and its result is not assigned elsewhere. + |It can be removed without changing the semantics of the program. This may indicate an error.""" } class TraitCompanionWithMutableStatic()(using Context) extends SyntaxMsg(TraitCompanionWithMutableStaticID) { - def msg = em"Companion of traits cannot define mutable @static fields" + def msg = e"Companion of traits cannot define mutable @static fields" def explain = "" } class LazyStaticField()(using Context) extends SyntaxMsg(LazyStaticFieldID) { - def msg = em"Lazy @static fields are not supported" + def msg = e"Lazy @static fields are not supported" def explain = "" } class StaticOverridingNonStaticMembers()(using Context) extends SyntaxMsg(StaticOverridingNonStaticMembersID) { - def msg = em"${hl("@static")} members cannot override or implement non-static ones" + def msg = e"${hl("@static")} members cannot override or implement non-static ones" def explain = "" } @@ -2230,21 +2229,21 @@ import cc.CaptureSet.IdentityCaptRefMap extends DeclarationMsg(OverloadInRefinementID) { def msg = "Refinements cannot introduce overloaded definitions" def explain = - em"""The refinement `$rsym` introduces an overloaded definition. - |Refinements cannot contain overloaded definitions.""".stripMargin + e"""The refinement `$rsym` introduces an overloaded definition. + |Refinements cannot contain overloaded definitions.""" } class NoMatchingOverload(val alternatives: List[SingleDenotation], pt: Type)(using Context) extends TypeMsg(NoMatchingOverloadID) { def msg = - em"""None of the ${err.overloadedAltsStr(alternatives)} - |match ${err.expectedTypeStr(pt)}""" + e"""None of the ${err.overloadedAltsStr(alternatives)} + |match ${err.expectedTypeStr(pt)}""" def explain = "" } class StableIdentPattern(tree: untpd.Tree, pt: Type)(using Context) extends TypeMsg(StableIdentPatternID) { def msg = - em"""Stable identifier required, but $tree found""" + e"""Stable identifier required, but $tree found""" def explain = "" } @@ -2299,7 +2298,7 @@ import cc.CaptureSet.IdentityCaptRefMap | ${accMixin.name} comes before ${otherMixin.name}. |2. Alternatively, replace $superCall in the body of $accMixin by a | super-call to a specific parent, e.g. $staticSuperCall - |""".stripMargin + |""" } def explain = "" } @@ -2316,20 +2315,20 @@ import cc.CaptureSet.IdentityCaptRefMap | |In order to fix this issue consider directly extending from the parent rather |than obtaining it from the parameters of ${cls.show}. - |""".stripMargin + |""" } class UnknownNamedEnclosingClassOrObject(name: TypeName)(using Context) extends ReferenceMsg(UnknownNamedEnclosingClassOrObjectID) { def msg = - em"""no enclosing class or object is named '${hl(name.show)}'""" + e"""no enclosing class or object is named '${hl(name.show)}'""" def explain = ex""" |The class or object named '${hl(name.show)}' was used as a visibility |modifier, but could not be resolved. Make sure that |'${hl(name.show)}' is not misspelled and has been imported into the |current scope. - """.stripMargin + """ } class IllegalCyclicTypeReference(sym: Symbol, where: String, lastChecked: Type)(using Context) @@ -2351,12 +2350,12 @@ import cc.CaptureSet.IdentityCaptRefMap class CaseClassMissingNonImplicitParamList(cdef: untpd.TypeDef)(using Context) extends SyntaxMsg(CaseClassMissingNonImplicitParamListID) { def msg = - em"""|A ${hl("case class")} must have at least one leading non-implicit parameter list""" + e"""|A ${hl("case class")} must have at least one leading non-implicit parameter list""" def explain = - em"""|${cdef.name} must have at least one leading non-implicit parameter list, - | if you're aiming to have a case class parametrized only by implicit ones, you should - | add an explicit ${hl("()")} as the first parameter list to ${cdef.name}.""".stripMargin + e"""|${cdef.name} must have at least one leading non-implicit parameter list, + | if you're aiming to have a case class parametrized only by implicit ones, you should + | add an explicit ${hl("()")} as the first parameter list to ${cdef.name}.""" } class EnumerationsShouldNotBeEmpty(cdef: untpd.TypeDef)(using Context) @@ -2364,12 +2363,12 @@ import cc.CaptureSet.IdentityCaptRefMap def msg = "Enumerations must contain at least one case" def explain = - em"""|Enumeration ${cdef.name} must contain at least one case - |Example Usage: - | ${hl("enum")} ${cdef.name} { - | ${hl("case")} Option1, Option2 - | } - |""".stripMargin + e"""|Enumeration ${cdef.name} must contain at least one case + |Example Usage: + | ${hl("enum")} ${cdef.name} { + | ${hl("case")} Option1, Option2 + | } + |""" } class TypedCaseDoesNotExplicitlyExtendTypedEnum(enumDef: Symbol, caseDef: untpd.TypeDef)(using Context) @@ -2377,32 +2376,32 @@ import cc.CaptureSet.IdentityCaptRefMap def msg = i"explicit extends clause needed because both enum case and enum class have type parameters" def explain = - em"""Enumerations where the enum class as well as the enum case have type parameters need - |an explicit extends. - |for example: - | ${hl("enum")} ${enumDef.name}[T] { - | ${hl("case")} ${caseDef.name}[U](u: U) ${hl("extends")} ${enumDef.name}[U] - | } - |""".stripMargin + e"""Enumerations where the enum class as well as the enum case have type parameters need + |an explicit extends. + |for example: + | ${hl("enum")} ${enumDef.name}[T] { + | ${hl("case")} ${caseDef.name}[U](u: U) ${hl("extends")} ${enumDef.name}[U] + | } + |""" } class IllegalRedefinitionOfStandardKind(kindType: String, name: Name)(using Context) extends SyntaxMsg(IllegalRedefinitionOfStandardKindID) { - def msg = em"illegal redefinition of standard $kindType $name" + def msg = e"illegal redefinition of standard $kindType $name" def explain = - em"""| "$name" is a standard Scala core `$kindType` - | Please choose a different name to avoid conflicts - |""".stripMargin + e"""| "$name" is a standard Scala core `$kindType` + | Please choose a different name to avoid conflicts + |""" } class NoExtensionMethodAllowed(mdef: untpd.DefDef)(using Context) extends SyntaxMsg(NoExtensionMethodAllowedID) { - def msg = em"No extension method allowed here, since collective parameters are given" + def msg = e"No extension method allowed here, since collective parameters are given" def explain = - em"""|Extension method: - | `${mdef}` - |is defined inside an extension clause which has collective parameters. - |""".stripMargin + e"""|Extension method: + | `${mdef}` + |is defined inside an extension clause which has collective parameters. + |""" } class ExtensionMethodCannotHaveTypeParams(mdef: untpd.DefDef)(using Context) @@ -2410,67 +2409,67 @@ import cc.CaptureSet.IdentityCaptRefMap def msg = i"Extension method cannot have type parameters since some were already given previously" def explain = - em"""|Extension method: - | `${mdef}` - |has type parameters `[${mdef.leadingTypeParams.map(_.show).mkString(",")}]`, while the extension clause has - |it's own type parameters. Please consider moving these to the extension clause's type parameter list. - |""".stripMargin + e"""|Extension method: + | `${mdef}` + |has type parameters `[${mdef.leadingTypeParams.map(_.show).mkString(",")}]`, while the extension clause has + |it's own type parameters. Please consider moving these to the extension clause's type parameter list. + |""" } class ExtensionCanOnlyHaveDefs(mdef: untpd.Tree)(using Context) extends SyntaxMsg(ExtensionCanOnlyHaveDefsID) { - def msg = em"Only methods allowed here, since collective parameters are given" + def msg = e"Only methods allowed here, since collective parameters are given" def explain = - em"""Extension clauses can only have `def`s - | `${mdef.show}` is not a valid expression here. - |""".stripMargin + e"""Extension clauses can only have `def`s + | `${mdef.show}` is not a valid expression here. + |""" } class UnexpectedPatternForSummonFrom(tree: Tree[_])(using Context) extends SyntaxMsg(UnexpectedPatternForSummonFromID) { - def msg = em"Unexpected pattern for summonFrom. Expected ${hl("`x: T`")} or ${hl("`_`")}" + def msg = e"Unexpected pattern for summonFrom. Expected ${hl("`x: T`")} or ${hl("`_`")}" def explain = - em"""|The pattern "${tree.show}" provided in the ${hl("case")} expression of the ${hl("summonFrom")}, - | needs to be of the form ${hl("`x: T`")} or ${hl("`_`")}. - | - | Example usage: - | inline def a = summonFrom { - | case x: T => ??? - | } - | - | or - | inline def a = summonFrom { - | case _ => ??? - | } - |""".stripMargin + e"""|The pattern "${tree.show}" provided in the ${hl("case")} expression of the ${hl("summonFrom")}, + | needs to be of the form ${hl("`x: T`")} or ${hl("`_`")}. + | + | Example usage: + | inline def a = summonFrom { + | case x: T => ??? + | } + | + | or + | inline def a = summonFrom { + | case _ => ??? + | } + |""" } class AnonymousInstanceCannotBeEmpty(impl: untpd.Template)(using Context) extends SyntaxMsg(AnonymousInstanceCannotBeEmptyID) { def msg = i"anonymous instance must implement a type or have at least one extension method" def explain = - em"""|Anonymous instances cannot be defined with an empty body. The block - |`${impl.show}` should either contain an implemented type or at least one extension method. - |""".stripMargin + e"""|Anonymous instances cannot be defined with an empty body. The block + |`${impl.show}` should either contain an implemented type or at least one extension method. + |""" } class ModifierNotAllowedForDefinition(flag: Flag)(using Context) extends SyntaxMsg(ModifierNotAllowedForDefinitionID) { - def msg = em"Modifier ${hl(flag.flagsString)} is not allowed for this definition" + def msg = e"Modifier ${hl(flag.flagsString)} is not allowed for this definition" def explain = "" } class RedundantModifier(flag: Flag)(using Context) extends SyntaxMsg(RedundantModifierID) { - def msg = em"Modifier ${hl(flag.flagsString)} is redundant for this definition" + def msg = e"Modifier ${hl(flag.flagsString)} is redundant for this definition" def explain = "" } class InvalidReferenceInImplicitNotFoundAnnotation(typeVar: String, owner: String)(using Context) extends ReferenceMsg(InvalidReferenceInImplicitNotFoundAnnotationID) { - def msg = em"""|Invalid reference to a type variable ${hl(typeVar)} found in the annotation argument. - |The variable does not occur as a parameter in the scope of ${hl(owner)}. - |""".stripMargin + def msg = e"""|Invalid reference to a type variable ${hl(typeVar)} found in the annotation argument. + |The variable does not occur as a parameter in the scope of ${hl(owner)}. + |""" def explain = "" } @@ -2480,9 +2479,9 @@ import cc.CaptureSet.IdentityCaptRefMap def defKind = if tree.symbol.is(Module) then "object" else "class" def msg = s"Case $defKind definitions are not allowed in inline methods or quoted code. Use a normal $defKind instead." def explain = - em"""Case class/object definitions generate a considerable footprint in code size. - |Inlining such definition would multiply this footprint for each call site. - |""".stripMargin + e"""Case class/object definitions generate a considerable footprint in code size. + |Inlining such definition would multiply this footprint for each call site. + |""" } class ImplicitSearchTooLargeWarning(limit: Int, openSearchPairs: List[(Candidate, Type)])(using Context) @@ -2491,43 +2490,43 @@ import cc.CaptureSet.IdentityCaptRefMap def showQuery(query: (Candidate, Type)): String = i" ${query._1.ref.symbol.showLocated} for ${query._2}}" def msg = - em"""Implicit search problem too large. - |an implicit search was terminated with failure after trying $limit expressions. - |The root candidate for the search was: - | - |${showQuery(openSearchPairs.last)} - | - |You can change the behavior by setting the `-Ximplicit-search-limit` value. - |Smaller values cause the search to fail faster. - |Larger values might make a very large search problem succeed. - |""" + e"""Implicit search problem too large. + |an implicit search was terminated with failure after trying $limit expressions. + |The root candidate for the search was: + | + |${showQuery(openSearchPairs.last)} + | + |You can change the behavior by setting the `-Ximplicit-search-limit` value. + |Smaller values cause the search to fail faster. + |Larger values might make a very large search problem succeed. + |""" def explain = - em"""The overflow happened with the following lists of tried expressions and target types, - |starting with the root query: - | - |${openSearchPairs.reverse.map(showQuery)}%\n% + e"""The overflow happened with the following lists of tried expressions and target types, + |starting with the root query: + | + |${openSearchPairs.reverse.map(showQuery)}%\n% """ class TargetNameOnTopLevelClass(symbol: Symbol)(using Context) extends SyntaxMsg(TargetNameOnTopLevelClassID): - def msg = em"${hl("@targetName")} annotation not allowed on top-level $symbol" + def msg = e"${hl("@targetName")} annotation not allowed on top-level $symbol" def explain = val annot = symbol.getAnnotation(defn.TargetNameAnnot).get - em"""The @targetName annotation may be applied to a top-level ${hl("val")} or ${hl("def")}, but not - |a top-level ${hl("class")}, ${hl("trait")}, or ${hl("object")}. - | - |This restriction is due to the naming convention of Java classfiles, whose filenames - |are based on the name of the class defined within. If @targetName were permitted - |here, the name of the classfile would be based on the target name, and the compiler - |could not associate that classfile with the Scala-visible defined name of the class. - | - |If your use case requires @targetName, consider wrapping $symbol in an ${hl("object")} - |(and possibly exporting it), as in the following example: - | - |${hl("object Wrapper:")} - | $annot $symbol { ... } - | - |${hl("export")} Wrapper.${symbol.name} ${hl("// optional")}""" + e"""The @targetName annotation may be applied to a top-level ${hl("val")} or ${hl("def")}, but not + |a top-level ${hl("class")}, ${hl("trait")}, or ${hl("object")}. + | + |This restriction is due to the naming convention of Java classfiles, whose filenames + |are based on the name of the class defined within. If @targetName were permitted + |here, the name of the classfile would be based on the target name, and the compiler + |could not associate that classfile with the Scala-visible defined name of the class. + | + |If your use case requires @targetName, consider wrapping $symbol in an ${hl("object")} + |(and possibly exporting it), as in the following example: + | + |${hl("object Wrapper:")} + | $annot $symbol { ... } + | + |${hl("export")} Wrapper.${symbol.name} ${hl("// optional")}""" class NotClassType(tp: Type)(using Context) extends TypeMsg(NotClassTypeID), ShowMatchTrace(tp): diff --git a/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala b/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala index 6b0a4c3e9737..b63773687f74 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala @@ -67,8 +67,8 @@ class CheckReentrant extends MiniPhase { if (sym.isTerm && !sym.isSetter && !isIgnored(sym)) if (sym.is(Mutable)) { report.error( - i"""possible data race involving globally reachable ${sym.showLocated}: ${sym.info} - | use -Ylog:checkReentrant+ to find out more about why the variable is reachable.""") + em"""possible data race involving globally reachable ${sym.showLocated}: ${sym.info} + | use -Ylog:checkReentrant+ to find out more about why the variable is reachable.""") shared += sym } else if (!sym.is(Method) || sym.isOneOf(Accessor | ParamAccessor)) diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index c797c9fd92c1..b17cc3e9bb94 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -556,8 +556,9 @@ object Erasure { if tree.symbol.is(Flags.Inline) then em"""${tree.symbol} is declared as `inline`, but was not inlined | - |Try increasing `-Xmax-inlines` above ${ctx.settings.XmaxInlines.value}""".stripMargin - else em"${tree.symbol} is declared as `erased`, but is in fact used" + |Try increasing `-Xmax-inlines` above ${ctx.settings.XmaxInlines.value}""" + else + em"${tree.symbol} is declared as `erased`, but is in fact used" report.error(msg, tree.srcPos) tree.symbol.getAnnotation(defn.CompileTimeOnlyAnnot) match { case Some(annot) => diff --git a/compiler/src/dotty/tools/dotc/transform/Pickler.scala b/compiler/src/dotty/tools/dotc/transform/Pickler.scala index 4d9b42a36fe7..b862ca7fe01e 100644 --- a/compiler/src/dotty/tools/dotc/transform/Pickler.scala +++ b/compiler/src/dotty/tools/dotc/transform/Pickler.scala @@ -11,7 +11,7 @@ import Periods._ import Phases._ import Symbols._ import Flags.Module -import reporting.{ThrowingReporter, Profile} +import reporting.{ThrowingReporter, Profile, Message} import collection.mutable import scala.concurrent.{Future, Await, ExecutionContext} import scala.concurrent.duration.Duration @@ -71,7 +71,7 @@ class Pickler extends Phase { val treePkl = new TreePickler(pickler) treePkl.pickle(tree :: Nil) Profile.current.recordTasty(treePkl.buf.length) - val positionWarnings = new mutable.ListBuffer[String]() + val positionWarnings = new mutable.ListBuffer[Message]() val pickledF = inContext(ctx.fresh) { Future { treePkl.compactify() @@ -147,8 +147,8 @@ class Pickler extends Phase { if unequal then output("before-pickling.txt", previous) output("after-pickling.txt", unpickled) - report.error(s"""pickling difference for $cls in ${cls.source}, for details: - | - | diff before-pickling.txt after-pickling.txt""".stripMargin) + report.error(em"""pickling difference for $cls in ${cls.source}, for details: + | + | diff before-pickling.txt after-pickling.txt""") end testSame } diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index dc8defa90eef..7551306d8b70 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -169,7 +169,7 @@ class TreeChecker extends Phase with SymTransformer { everDefinedSyms.get(sym) match { case Some(t) => if (t ne tree) - report.warning(i"symbol ${sym.fullName} is defined at least twice in different parts of AST") + report.warning(em"symbol ${sym.fullName} is defined at least twice in different parts of AST") // should become an error case None => everDefinedSyms(sym) = tree @@ -179,7 +179,7 @@ class TreeChecker extends Phase with SymTransformer { if (ctx.settings.YcheckMods.value) tree match { case t: untpd.MemberDef => - if (t.name ne sym.name) report.warning(s"symbol ${sym.fullName} name doesn't correspond to AST: ${t}") + if (t.name ne sym.name) report.warning(em"symbol ${sym.fullName} name doesn't correspond to AST: ${t}") // todo: compare trees inside annotations case _ => } diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index ec72c48b2422..091792dcd1fe 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -563,7 +563,7 @@ trait Applications extends Compatibility { i"it is not the only argument to be passed to the corresponding repeated parameter $formal" else i"the corresponding parameter has type $formal which is not a repeated parameter type" - fail(em"Sequence argument type annotation `*` cannot be used here:\n$addendum".toMessage, arg) + fail(em"Sequence argument type annotation `*` cannot be used here:\n$addendum", arg) /** Add result of typing argument `arg` against parameter type `formal`. * @return The remaining formal parameter types. If the method is parameter-dependent diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 99399832085f..e7d9dee8998c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -496,15 +496,15 @@ object Checking { } if sym.is(Transparent) then if sym.isType then - if !sym.isExtensibleClass then fail(em"`transparent` can only be used for extensible classes and traits".toMessage) + if !sym.isExtensibleClass then fail(em"`transparent` can only be used for extensible classes and traits") else - if !sym.isInlineMethod then fail(em"`transparent` can only be used for inline methods".toMessage) + if !sym.isInlineMethod then fail(em"`transparent` can only be used for inline methods") if (!sym.isClass && sym.is(Abstract)) fail(OnlyClassesCanBeAbstract(sym)) // note: this is not covered by the next test since terms can be abstract (which is a dual-mode flag) // but they can never be one of ClassOnlyFlags if !sym.isClass && sym.isOneOf(ClassOnlyFlags) then - fail(em"only classes can be ${(sym.flags & ClassOnlyFlags).flagsString}".toMessage) + fail(em"only classes can be ${(sym.flags & ClassOnlyFlags).flagsString}") if (sym.is(AbsOverride) && !sym.owner.is(Trait)) fail(AbstractOverrideOnlyInTraits(sym)) if sym.is(Trait) then @@ -600,7 +600,7 @@ object Checking { */ def checkNoPrivateLeaks(sym: Symbol)(using Context): Type = { class NotPrivate extends TypeMap { - var errors: List[() => String] = Nil + var errors: List[Message] = Nil private var inCaptureSet: Boolean = false def accessBoundary(sym: Symbol): Symbol = @@ -632,7 +632,7 @@ object Checking { var tp1 = if (isLeaked(tp.symbol)) { errors = - (() => em"non-private ${sym.showLocated} refers to private ${tp.symbol}\nin its type signature ${sym.info}") + em"non-private ${sym.showLocated} refers to private ${tp.symbol}\nin its type signature ${sym.info}" :: errors tp } @@ -673,7 +673,7 @@ object Checking { } val notPrivate = new NotPrivate val info = notPrivate(sym.info) - notPrivate.errors.foreach(error => report.errorOrMigrationWarning(error(), sym.srcPos, from = `3.0`)) + notPrivate.errors.foreach(report.errorOrMigrationWarning(_, sym.srcPos, from = `3.0`)) info } @@ -843,9 +843,9 @@ trait Checking { case Select(id, _) => id case _ => EmptyTree if extractor.isEmpty then - em"pattern binding uses refutable extractor" + e"pattern binding uses refutable extractor" else - em"pattern binding uses refutable extractor `$extractor`" + e"pattern binding uses refutable extractor `$extractor`" val fix = if isPatDef then "adding `: @unchecked` after the expression" @@ -935,8 +935,8 @@ trait Checking { // we restrict wildcard export from package as incremental compilation does not yet // register a dependency on "all members of a package" - see https://github.com/sbt/zinc/issues/226 report.error( - em"Implementation restriction: ${path.tpe.classSymbol} is not a valid prefix " + - "for a wildcard export, as it is a package.", path.srcPos) + em"Implementation restriction: ${path.tpe.classSymbol} is not a valid prefix for a wildcard export, as it is a package", + path.srcPos) /** Check that module `sym` does not clash with a class of the same name * that is concurrently compiled in another source file. diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index b2e0a4481297..6fa1e3b07e4a 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -87,18 +87,18 @@ object ErrorReporting { def expectedTypeStr(tp: Type): String = tp match { case tp: PolyProto => - em"type arguments [${tp.targs.tpes}%, %] and ${expectedTypeStr(revealDeepenedArgs(tp.resultType))}" + e"type arguments [${tp.targs.tpes}%, %] and ${expectedTypeStr(revealDeepenedArgs(tp.resultType))}" case tp: FunProto => def argStr(tp: FunProto): String = val result = revealDeepenedArgs(tp.resultType) match { case restp: FunProto => argStr(restp) case _: WildcardType | _: IgnoredProto => "" - case tp => em" and expected result type $tp" + case tp => e" and expected result type $tp" } - em"(${tp.typedArgs().tpes}%, %)$result" + e"(${tp.typedArgs().tpes}%, %)$result" s"arguments ${argStr(tp)}" case _ => - em"expected type $tp" + e"expected type $tp" } def anonymousTypeMemberStr(tpe: Type): String = { @@ -107,12 +107,12 @@ object ErrorReporting { case _: MethodOrPoly => "method" case _ => "value of type" } - em"$kind $tpe" + e"$kind $tpe" } def overloadedAltsStr(alts: List[SingleDenotation]): String = - em"overloaded alternatives of ${denotStr(alts.head)} with types\n" + - em" ${alts map (_.info)}%\n %" + e"overloaded alternatives of ${denotStr(alts.head)} with types\n" + + e" ${alts map (_.info)}%\n %" def denotStr(denot: Denotation): String = if (denot.isOverloaded) overloadedAltsStr(denot.alternatives) @@ -443,7 +443,7 @@ class ImplicitSearchError( private def hiddenImplicitsAddendum: String = def hiddenImplicitNote(s: SearchSuccess) = - em"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." + e"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." val normalImports = ignoredInstanceNormalImport.map(hiddenImplicitNote) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 0400d241e367..1229ba2dcc33 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -435,13 +435,13 @@ object Implicits: final protected def qualify(using Context): String = expectedType match { case SelectionProto(name, mproto, _, _) if !argument.isEmpty => - em"provide an extension method `$name` on ${argument.tpe}" + e"provide an extension method `$name` on ${argument.tpe}" case NoType => - if (argument.isEmpty) em"match expected type" - else em"convert from ${argument.tpe} to expected type" + if (argument.isEmpty) e"match expected type" + else e"convert from ${argument.tpe} to expected type" case _ => - if (argument.isEmpty) em"match type ${clarify(expectedType)}" - else em"convert from ${argument.tpe} to ${clarify(expectedType)}" + if (argument.isEmpty) e"match type ${clarify(expectedType)}" + else e"convert from ${argument.tpe} to ${clarify(expectedType)}" } /** An explanation of the cause of the failure as a string */ @@ -489,7 +489,7 @@ object Implicits: } def explanation(using Context): String = - em"no implicit values were found that $qualify" + e"no implicit values were found that $qualify" override def toString = s"NoMatchingImplicits($expectedType, $argument)" } @@ -522,7 +522,7 @@ object Implicits: if str1 == str2 then str1 = ctx.printer.toTextRef(alt1.ref).show str2 = ctx.printer.toTextRef(alt2.ref).show - em"both $str1 and $str2 $qualify" + e"both $str1 and $str2 $qualify" override def whyNoConversion(using Context): String = if !argument.isEmpty && argument.tpe.widen.isRef(defn.NothingClass) then "" @@ -537,20 +537,20 @@ object Implicits: val expectedType: Type, val argument: Tree) extends SearchFailureType { def explanation(using Context): String = - em"${err.refStr(ref)} does not $qualify" + e"${err.refStr(ref)} does not $qualify" } class DivergingImplicit(ref: TermRef, val expectedType: Type, val argument: Tree) extends SearchFailureType { def explanation(using Context): String = - em"${err.refStr(ref)} produces a diverging implicit search when trying to $qualify" + e"${err.refStr(ref)} produces a diverging implicit search when trying to $qualify" } /** A search failure type for attempted ill-typed extension method calls */ class FailedExtension(extApp: Tree, val expectedType: Type, val whyFailed: Message) extends SearchFailureType: def argument = EmptyTree - def explanation(using Context) = em"$extApp does not $qualify" + def explanation(using Context) = e"$extApp does not $qualify" /** A search failure type for aborted searches of extension methods, typically * because of a cyclic reference or similar. @@ -570,7 +570,7 @@ object Implicits: else reasons.mkString(" ", "", "") - def explanation(using Context) = em"Failed to synthesize an instance of type ${clarify(expectedType)}:${formatReasons}" + def explanation(using Context) = e"Failed to synthesize an instance of type ${clarify(expectedType)}:${formatReasons}" end Implicits @@ -939,10 +939,10 @@ trait Implicits: val qt = qual.tpe.widen val qt1 = qt.dealiasKeepAnnots def addendum = if (qt1 eq qt) "" else (i"\nThe required type is an alias of: $qt1") - em"parameter of ${qual.tpe.widen}$addendum" + e"parameter of ${qual.tpe.widen}$addendum" case _ => - em"${ if paramName.is(EvidenceParamName) then "an implicit parameter" - else s"parameter $paramName" } of $methodStr" + e"${ if paramName.is(EvidenceParamName) then "an implicit parameter" + else s"parameter $paramName" } of $methodStr" } /** A CanEqual[T, U] instance is assumed diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 6aab561c44b7..a9570fe9ebc1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -462,7 +462,7 @@ class Namer { typer: Typer => if isProvisional then typr.println(i"provisional superclass $first for $cls") first = AnnotatedType(first, Annotation(defn.ProvisionalSuperClassAnnot)) - checkFeasibleParent(first, cls.srcPos, em" in inferred superclass $first") :: parents + checkFeasibleParent(first, cls.srcPos, e" in inferred superclass $first") :: parents end ensureFirstIsClass /** Add child annotation for `child` to annotations of `cls`. The annotation diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index fa29f450be2a..87a4c4517bb2 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -54,7 +54,7 @@ trait QuotesAndSplices { val msg = em"""Quoted types `'[..]` can only be used in patterns. | |Hint: To get a scala.quoted.Type[T] use scala.quoted.Type.of[T] instead. - |""".stripMargin + |""" report.error(msg, tree.srcPos) EmptyTree else diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 8afcec4dee63..e44a81ed7e32 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -816,7 +816,7 @@ object RefChecks { for (baseCls <- caseCls.info.baseClasses.tail) if (baseCls.typeParams.exists(_.paramVarianceSign != 0)) for (problem <- variantInheritanceProblems(baseCls, caseCls, "non-variant", "case ")) - report.errorOrMigrationWarning(problem(), clazz.srcPos, from = `3.0`) + report.errorOrMigrationWarning(problem, clazz.srcPos, from = `3.0`) checkNoAbstractMembers() if (abstractErrors.isEmpty) checkNoAbstractDecls(clazz) @@ -847,7 +847,7 @@ object RefChecks { if cls.paramAccessors.nonEmpty && !mixins.contains(cls) problem <- variantInheritanceProblems(cls, clazz.asClass.superClass, "parameterized", "super") } - report.error(problem(), clazz.srcPos) + report.error(problem, clazz.srcPos) } checkParameterizedTraitsOK() @@ -861,13 +861,13 @@ object RefChecks { * Return an optional by name error message if this test fails. */ def variantInheritanceProblems( - baseCls: Symbol, middle: Symbol, baseStr: String, middleStr: String): Option[() => String] = { + baseCls: Symbol, middle: Symbol, baseStr: String, middleStr: String): Option[Message] = { val superBT = self.baseType(middle) val thisBT = self.baseType(baseCls) val combinedBT = superBT.baseType(baseCls) if (combinedBT =:= thisBT) None // ok else - Some(() => + Some( em"""illegal inheritance: $clazz inherits conflicting instances of $baseStr base $baseCls. | | Direct basetype: $thisBT diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index b90409e72364..fb14b242beac 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -177,16 +177,16 @@ trait TypeAssigner { val alts = tpe.denot.alternatives.map(_.symbol).filter(_.exists) val whatCanNot = alts match case Nil => - em"$name cannot" + e"$name cannot" case sym :: Nil => - em"${if (sym.owner == pre.typeSymbol) sym.show else sym.showLocated} cannot" + e"${if (sym.owner == pre.typeSymbol) sym.show else sym.showLocated} cannot" case _ => - em"none of the overloaded alternatives named $name can" + e"none of the overloaded alternatives named $name can" val where = if (ctx.owner.exists) s" from ${ctx.owner.enclosingClass}" else "" val whyNot = new StringBuffer alts.foreach(_.isAccessibleFrom(pre, superAccess, whyNot)) if tpe.isError then tpe - else errorType(ex"$whatCanNot be accessed as a member of $pre$where.$whyNot", pos) + else errorType(e"$whatCanNot be accessed as a member of $pre$where.$whyNot", pos) def processAppliedType(tree: untpd.Tree, tp: Type)(using Context): Type = tp match case AppliedType(tycon, args) => diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 032bed38482c..5e3295f44cb6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -283,7 +283,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer def checkUnambiguous(found: Type) = val other = recur(selectors.tail) if other.exists && found.exists && found != other then - fail(em"reference to `$name` is ambiguous; it is imported twice".toMessage) + fail(em"reference to `$name` is ambiguous; it is imported twice") found if selector.rename == termName && selector.rename != nme.WILDCARD then diff --git a/compiler/test/dotty/tools/dotc/StringFormatterTest.scala b/compiler/test/dotty/tools/dotc/StringFormatterTest.scala index e745fa515443..ec81011eecc8 100644 --- a/compiler/test/dotty/tools/dotc/StringFormatterTest.scala +++ b/compiler/test/dotty/tools/dotc/StringFormatterTest.scala @@ -39,20 +39,20 @@ class StringFormatterTest extends AbstractStringFormatterTest: assertEquals("flags=private final ", store.string) end StringFormatterTest -class EmStringFormatterTest extends AbstractStringFormatterTest: - @Test def seq = check("[Any, String]", em"${Seq(defn.AnyType, defn.StringType)}") - @Test def seqSeq = check("Any; String", em"${Seq(defn.AnyType, defn.StringType)}%; %") - @Test def ellipsis = assert(em"$Big".contains("...")) - @Test def err = check("type Err", em"$Err") - @Test def ambig = check("Foo vs Foo", em"$Foo vs $Foo") - @Test def cstrd = check("Foo; Bar", em"$mkCstrd%; %") - @Test def seqErr = check("[class Any, type Err]", em"${Seq(defn.AnyClass, Err)}") - @Test def seqSeqErr = check("class Any; type Err", em"${Seq(defn.AnyClass, Err)}%; %") - @Test def tupleErr = check("(1,type Err)", em"${(1, Err)}") - @Test def tupleAmb = check("(Foo,Foo)", em"${(Foo, Foo)}") - @Test def tupleFlags = check("(Foo,abstract)", em"${(Foo, Abstract)}") - @Test def seqOfTupleFlags = check("[(Foo,abstract)]", em"${Seq((Foo, Abstract))}") -end EmStringFormatterTest +class EStringFormatterTest extends AbstractStringFormatterTest: + @Test def seq = check("[Any, String]", e"${Seq(defn.AnyType, defn.StringType)}") + @Test def seqSeq = check("Any; String", e"${Seq(defn.AnyType, defn.StringType)}%; %") + @Test def ellipsis = assert(e"$Big".contains("...")) + @Test def err = check("type Err", e"$Err") + @Test def ambig = check("Foo vs Foo", e"$Foo vs $Foo") + @Test def cstrd = check("Foo; Bar", e"$mkCstrd%; %") + @Test def seqErr = check("[class Any, type Err]", e"${Seq(defn.AnyClass, Err)}") + @Test def seqSeqErr = check("class Any; type Err", e"${Seq(defn.AnyClass, Err)}%; %") + @Test def tupleErr = check("(1,type Err)", e"${(1, Err)}") + @Test def tupleAmb = check("(Foo,Foo)", e"${(Foo, Foo)}") + @Test def tupleFlags = check("(Foo,abstract)", e"${(Foo, Abstract)}") + @Test def seqOfTupleFlags = check("[(Foo,abstract)]", e"${Seq((Foo, Abstract))}") +end EStringFormatterTest class ExStringFormatterTest extends AbstractStringFormatterTest: @Test def seq = check("[Any, String]", ex"${Seq(defn.AnyType, defn.StringType)}") From ff705aad750c4015d61a64f411a5e12492f1e106 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 21 Nov 2022 11:56:27 +0100 Subject: [PATCH 02/14] Refactor TypeErrors Let them take Messages instead of Strings. We now are in a situation where the `e` interpolator is only used within `messages` itself. This means we can hopefully roll its behavior into the `Message` logic. --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 2 +- .../tools/dotc/core/ConstraintHandling.scala | 4 +- .../dotty/tools/dotc/core/Decorators.scala | 6 +- .../dotty/tools/dotc/core/Denotations.scala | 12 +-- .../tools/dotc/core/SymDenotations.scala | 9 +- .../dotty/tools/dotc/core/SymbolLoaders.scala | 4 +- .../dotty/tools/dotc/core/TypeComparer.scala | 2 +- .../dotty/tools/dotc/core/TypeErrors.scala | 89 ++++++++++--------- .../src/dotty/tools/dotc/core/TypeEval.scala | 2 +- .../src/dotty/tools/dotc/core/Types.scala | 12 +-- .../tools/dotc/core/tasty/TreePickler.scala | 2 +- .../tools/dotc/core/tasty/TreeUnpickler.scala | 8 +- .../core/unpickleScala2/Scala2Erasure.scala | 4 +- .../dotty/tools/dotc/quoted/Interpreter.scala | 13 +-- .../dotty/tools/dotc/typer/Applications.scala | 2 +- 15 files changed, 89 insertions(+), 82 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 12f216ed87fa..f278b4b610db 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -428,7 +428,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { else val res = Select(TypeTree(pre), tp) if needLoad && !res.symbol.isStatic then - throw new TypeError(e"cannot establish a reference to $res") + throw TypeError(em"cannot establish a reference to $res") res def ref(sym: Symbol)(using Context): Tree = diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 4ed01a5fbe0d..555f5858fb3f 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -152,8 +152,8 @@ trait ConstraintHandling { return param LevelAvoidMap(0, maxLevel)(param) match case freshVar: TypeVar => freshVar.origin - case _ => throw new TypeError( - i"Could not decrease the nesting level of ${param} from ${nestingLevel(param)} to $maxLevel in $constraint") + case _ => throw TypeError( + em"Could not decrease the nesting level of ${param} from ${nestingLevel(param)} to $maxLevel in $constraint") def nonParamBounds(param: TypeParamRef)(using Context): TypeBounds = constraint.nonParamBounds(param) diff --git a/compiler/src/dotty/tools/dotc/core/Decorators.scala b/compiler/src/dotty/tools/dotc/core/Decorators.scala index 249f2707788c..bbbdbc8f5eba 100644 --- a/compiler/src/dotty/tools/dotc/core/Decorators.scala +++ b/compiler/src/dotty/tools/dotc/core/Decorators.scala @@ -269,8 +269,10 @@ object Decorators { catch case ex: CyclicReference => "... (caught cyclic reference) ..." case NonFatal(ex) - if !ctx.mode.is(Mode.PrintShowExceptions) && !ctx.settings.YshowPrintErrors.value => - val msg = ex match { case te: TypeError => te.toMessage case _ => ex.getMessage } + if !ctx.mode.is(Mode.PrintShowExceptions) && !ctx.settings.YshowPrintErrors.value => + val msg = ex match + case te: TypeError => te.toMessage.message + case _ => ex.getMessage s"[cannot display due to $msg, raw string = $x]" case _ => String.valueOf(x).nn diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index a56bdac67952..7922ba44bc20 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -300,9 +300,9 @@ object Denotations { case NoDenotation | _: NoQualifyingRef | _: MissingRef => def argStr = if (args.isEmpty) "" else i" matching ($args%, %)" val msg = - if (site.exists) i"$site does not have a member $kind $name$argStr" - else i"missing: $kind $name$argStr" - throw new TypeError(msg) + if site.exists then em"$site does not have a member $kind $name$argStr" + else em"missing: $kind $name$argStr" + throw TypeError(msg) case denot => denot.symbol } @@ -1268,9 +1268,9 @@ object Denotations { if sd1.exists then if sd2.exists then throw TypeError( - e"""Failure to disambiguate overloaded reference with - | ${denot1.symbol.showLocated}: ${denot1.info} and - | ${denot2.symbol.showLocated}: ${denot2.info}""") + em"""Failure to disambiguate overloaded reference with + | ${denot1.symbol.showLocated}: ${denot1.info} and + | ${denot2.symbol.showLocated}: ${denot2.info}""") else sd1 else sd2 } diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index f77bca95007f..7c1af7a434dc 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -168,7 +168,8 @@ object SymDenotations { } } else { - if (myFlags.is(Touched)) throw CyclicReference(this) + if (myFlags.is(Touched)) + throw CyclicReference(this)(using ctx.withOwner(symbol)) myFlags |= Touched atPhase(validFor.firstPhaseId)(completer.complete(this)) } @@ -2438,13 +2439,13 @@ object SymDenotations { val youngest = assocFiles.filter(_.lastModified == lastModDate) val chosen = youngest.head def ambiguousFilesMsg(f: AbstractFile) = - e"""Toplevel definition $name is defined in + i"""Toplevel definition $name is defined in | $chosen |and also in | $f""" if youngest.size > 1 then - throw TypeError(i"""${ambiguousFilesMsg(youngest.tail.head)} - |One of these files should be removed from the classpath.""") + throw TypeError(em"""${ambiguousFilesMsg(youngest.tail.head)} + |One of these files should be removed from the classpath.""") // Warn if one of the older files comes from a different container. // In that case picking the youngest file is not necessarily what we want, diff --git a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala index c5ae98853061..a2f697da1624 100644 --- a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -88,8 +88,8 @@ object SymbolLoaders { return NoSymbol } else - throw new TypeError( - i"""$owner contains object and package with same name: $pname + throw TypeError( + em"""$owner contains object and package with same name: $pname |one of them needs to be removed from classpath""") newModuleSymbol(owner, pname, PackageCreationFlags, PackageCreationFlags, completer).entered diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index d26f933e7547..a4c476568818 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -3164,7 +3164,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) { tp case Nil => val casesText = MatchTypeTrace.noMatchesText(scrut, cases) - throw new TypeError(s"Match type reduction $casesText") + throw TypeError(em"Match type reduction $casesText") inFrozenConstraint { // Empty types break the basic assumption that if a scrutinee and a diff --git a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala index a3b594eb0f09..bab0e0c61e5f 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala @@ -14,37 +14,44 @@ import reporting._ import ast.untpd import config.Printers.cyclicErrors -class TypeError(msg: String) extends Exception(msg) { - def this() = this("") - final def toMessage(using Context): Message = - withMode(Mode.Printing)(produceMessage) - def produceMessage(using Context): Message = super.getMessage.nn.toMessage - override def getMessage: String = super.getMessage.nn -} - -class MalformedType(pre: Type, denot: Denotation, absMembers: Set[Name]) extends TypeError { - override def produceMessage(using Context): Message = - i"malformed type: $pre is not a legal prefix for $denot because it contains abstract type member${if (absMembers.size == 1) "" else "s"} ${absMembers.mkString(", ")}" - .toMessage -} - -class MissingType(pre: Type, name: Name) extends TypeError { +abstract class TypeError(using creationContext: Context) extends Exception(""): + + /** Convert to message. This uses an additional Context, so that we + * use the context when the message is first produced, i.e. when the TypeError + * is handled. This makes a difference for CyclicErrors since we need to know + * the context where the completed symbol is referenced, but the creation + * context of the CyclicReference is the completion context for the symbol. + * See i2887b for a test case, where we want to see + * "recursive or overloaded method needs result type". + */ + def toMessage(using Context): Message + + /** Uses creation context to produce the message */ + override def getMessage: String = toMessage.message + +object TypeError: + def apply(msg: Message)(using Context) = new TypeError: + def toMessage(using Context) = msg +end TypeError + +class MalformedType(pre: Type, denot: Denotation, absMembers: Set[Name])(using Context) extends TypeError: + def toMessage(using Context) = em"malformed type: $pre is not a legal prefix for $denot because it contains abstract type member${if (absMembers.size == 1) "" else "s"} ${absMembers.mkString(", ")}" + +class MissingType(pre: Type, name: Name)(using Context) extends TypeError: private def otherReason(pre: Type)(using Context): String = pre match { case pre: ThisType if pre.cls.givenSelfType.exists => i"\nor the self type of $pre might not contain all transitive dependencies" case _ => "" } - override def produceMessage(using Context): Message = { - if (ctx.debug) printStackTrace() - i"""cannot resolve reference to type $pre.$name - |the classfile defining the type might be missing from the classpath${otherReason(pre)}""" - .toMessage - } -} + override def toMessage(using Context): Message = + if ctx.debug then printStackTrace() + em"""cannot resolve reference to type $pre.$name + |the classfile defining the type might be missing from the classpath${otherReason(pre)}""" +end MissingType -class RecursionOverflow(val op: String, details: => String, val previous: Throwable, val weight: Int) -extends TypeError { +class RecursionOverflow(val op: String, details: => String, val previous: Throwable, val weight: Int)(using Context) +extends TypeError: def explanation: String = s"$op $details" @@ -71,19 +78,18 @@ extends TypeError { (rs.map(_.explanation): List[String]).mkString("\n ", "\n| ", "") } - override def produceMessage(using Context): Message = NoExplanation { + override def toMessage(using Context): Message = val mostCommon = recursions.groupBy(_.op).toList.maxBy(_._2.map(_.weight).sum)._2.reverse - s"""Recursion limit exceeded. - |Maybe there is an illegal cyclic reference? - |If that's not the case, you could also try to increase the stacksize using the -Xss JVM option. - |For the unprocessed stack trace, compile with -Yno-decode-stacktraces. - |A recurring operation is (inner to outer): - |${opsString(mostCommon)}""".stripMargin - } + em"""Recursion limit exceeded. + |Maybe there is an illegal cyclic reference? + |If that's not the case, you could also try to increase the stacksize using the -Xss JVM option. + |For the unprocessed stack trace, compile with -Yno-decode-stacktraces. + |A recurring operation is (inner to outer): + |${opsString(mostCommon).stripMargin}""" override def fillInStackTrace(): Throwable = this override def getStackTrace(): Array[StackTraceElement] = previous.getStackTrace().asInstanceOf -} +end RecursionOverflow /** Post-process exceptions that might result from StackOverflow to add * tracing information while unwalking the stack. @@ -111,10 +117,10 @@ object handleRecursive { * so it requires knowing denot already. * @param denot */ -class CyclicReference private (val denot: SymDenotation) extends TypeError { +class CyclicReference private (val denot: SymDenotation)(using Context) extends TypeError: var inImplicitSearch: Boolean = false - override def produceMessage(using Context): Message = { + override def toMessage(using Context): Message = val cycleSym = denot.symbol // cycleSym.flags would try completing denot and would fail, but here we can use flagsUNSAFE to detect flags @@ -151,19 +157,16 @@ class CyclicReference private (val denot: SymDenotation) extends TypeError { CyclicReferenceInvolving(denot) errorMsg(ctx) - } -} + end toMessage -object CyclicReference { - def apply(denot: SymDenotation)(using Context): CyclicReference = { +object CyclicReference: + def apply(denot: SymDenotation)(using Context): CyclicReference = val ex = new CyclicReference(denot) - if (!(ctx.mode is Mode.CheckCyclic) || ctx.settings.Ydebug.value) { + if !(ctx.mode is Mode.CheckCyclic) || ctx.settings.Ydebug.value then cyclicErrors.println(s"Cyclic reference involving! $denot") val sts = ex.getStackTrace.asInstanceOf[Array[StackTraceElement]] for (elem <- sts take 200) cyclicErrors.println(elem.toString) - } ex - } -} +end CyclicReference diff --git a/compiler/src/dotty/tools/dotc/core/TypeEval.scala b/compiler/src/dotty/tools/dotc/core/TypeEval.scala index 7ec0f12db3b6..b5684b07f181 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeEval.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeEval.scala @@ -91,7 +91,7 @@ object TypeEval: val result = try op catch case e: Throwable => - throw new TypeError(e.getMessage.nn) + throw TypeError(em"${e.getMessage.nn}") ConstantType(Constant(result)) def constantFold1[T](extractor: Type => Option[T], op: T => Any): Option[Type] = diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 29a2496ab2a7..98a0c540323d 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2414,12 +2414,12 @@ object Types { } else { if (!ctx.reporter.errorsReported) - throw new TypeError( - i"""bad parameter reference $this at ${ctx.phase} - |the parameter is ${param.showLocated} but the prefix $prefix - |does not define any corresponding arguments. - |idx = $idx, args = $args%, %, - |constraint = ${ctx.typerState.constraint}""") + throw TypeError( + em"""bad parameter reference $this at ${ctx.phase} + |the parameter is ${param.showLocated} but the prefix $prefix + |does not define any corresponding arguments. + |idx = $idx, args = $args%, %, + |constraint = ${ctx.typerState.constraint}""") NoDenotation } } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 34c22439a932..d20391bce0a4 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -451,7 +451,7 @@ class TreePickler(pickler: TastyPickler) { withLength { pickleTree(qual); if (!mix.isEmpty) { - // mixinType being a TypeRef when mix is non-empty is enforced by TreeChecker#checkSuper + // mixinType being a TypeRef when mix is non-empty is enforced by TreeChecker#checkSuper val SuperType(_, mixinType: TypeRef) = tree.tpe: @unchecked pickleTree(mix.withType(mixinType)) } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 78ffaaacd452..617a2c55a7ad 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -127,10 +127,10 @@ class TreeUnpickler(reader: TastyReader, if f == null then "" else s" in $f" if ctx.settings.YdebugUnpickling.value then throw ex else throw TypeError( - e"""Could not read definition of $denot$where - |An exception was encountered: - | $ex - |Run with -Ydebug-unpickling to see full stack trace.""") + em"""Could not read definition of $denot$where + |An exception was encountered: + | $ex + |Run with -Ydebug-unpickling to see full stack trace.""") treeAtAddr(currentAddr) = try atPhaseBeforeTransforms { diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Erasure.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Erasure.scala index f2d25d0f34b5..cc2d7dd7ee56 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Erasure.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Erasure.scala @@ -39,9 +39,9 @@ object Scala2Erasure: case RefinedType(parent, _, _) => checkSupported(parent) case AnnotatedType(parent, _) if parent.dealias.isInstanceOf[Scala2RefinedType] => - throw new TypeError(i"Unsupported Scala 2 type: Component $parent of intersection is annotated.") + throw TypeError(em"Unsupported Scala 2 type: Component $parent of intersection is annotated.") case tp @ TypeRef(prefix, _) if !tp.symbol.exists && prefix.dealias.isInstanceOf[Scala2RefinedType] => - throw new TypeError(i"Unsupported Scala 2 type: Prefix $prefix of intersection component is an intersection or refinement.") + throw TypeError(em"Unsupported Scala 2 type: Prefix $prefix of intersection component is an intersection or refinement.") case _ => /** A type that would be represented as a RefinedType in Scala 2. diff --git a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala index 0d4afefd8fab..e205bef42b86 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala @@ -28,6 +28,7 @@ import dotty.tools.dotc.quoted._ import dotty.tools.dotc.transform.TreeMapWithStages._ import dotty.tools.dotc.typer.ImportInfo.withRootImports import dotty.tools.dotc.util.SrcPos +import dotty.tools.dotc.reporting.Message import dotty.tools.repl.AbstractFileClassLoader /** Tree interpreter for metaprogramming constructs */ @@ -60,7 +61,7 @@ abstract class Interpreter(pos: SrcPos, classLoader: ClassLoader)(using Context) case tree: Ident if tree.symbol.is(Inline, butNot = Method) => tree.tpe.widenTermRefExpr match case ConstantType(c) => c.value.asInstanceOf[Object] - case _ => throw new StopInterpretation(e"${tree.symbol} could not be inlined", tree.srcPos) + case _ => throw new StopInterpretation(em"${tree.symbol} could not be inlined", tree.srcPos) // TODO disallow interpreted method calls as arguments case Call(fn, args) => @@ -190,7 +191,7 @@ abstract class Interpreter(pos: SrcPos, classLoader: ClassLoader)(using Context) } private def unexpectedTree(tree: Tree)(implicit env: Env): Object = - throw new StopInterpretation("Unexpected tree could not be interpreted: " + tree, tree.srcPos) + throw new StopInterpretation(em"Unexpected tree could not be interpreted: ${tree.toString}", tree.srcPos) private def loadModule(sym: Symbol): Object = if (sym.owner.is(Package)) { @@ -231,7 +232,7 @@ abstract class Interpreter(pos: SrcPos, classLoader: ClassLoader)(using Context) try clazz.getMethod(name.toString, paramClasses: _*) catch { case _: NoSuchMethodException => - val msg = e"Could not find method ${clazz.getCanonicalName}.$name with parameters ($paramClasses%, %)" + val msg = em"Could not find method ${clazz.getCanonicalName}.$name with parameters ($paramClasses%, %)" throw new StopInterpretation(msg, pos) case MissingClassDefinedInCurrentRun(sym) if ctx.compilationUnit.isSuspendable => if (ctx.settings.XprintSuspension.value) @@ -249,7 +250,7 @@ abstract class Interpreter(pos: SrcPos, classLoader: ClassLoader)(using Context) sw.write("\n") ex.printStackTrace(new PrintWriter(sw)) sw.write("\n") - throw new StopInterpretation(sw.toString, pos) + throw new StopInterpretation(sw.toString.toMessage, pos) case ex: InvocationTargetException => ex.getTargetException match { case ex: scala.quoted.runtime.StopMacroExpansion => @@ -270,7 +271,7 @@ abstract class Interpreter(pos: SrcPos, classLoader: ClassLoader)(using Context) } targetException.printStackTrace(new PrintWriter(sw)) sw.write("\n") - throw new StopInterpretation(sw.toString, pos) + throw new StopInterpretation(sw.toString.toMessage, pos) } } @@ -344,7 +345,7 @@ end Interpreter object Interpreter: /** Exception that stops interpretation if some issue is found */ - class StopInterpretation(val msg: String, val pos: SrcPos) extends Exception + class StopInterpretation(val msg: Message, val pos: SrcPos) extends Exception object Call: import tpd._ diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 091792dcd1fe..b3828fc074bd 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1096,7 +1096,7 @@ trait Applications extends Compatibility { /** Overridden in ReTyper to handle primitive operations that can be generated after erasure */ protected def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(using Context): Tree = if ctx.reporter.errorsReported then - throw TypeError(i"unexpected function type: ${methPart(fun).tpe}") + throw TypeError(em"unexpected function type: ${methPart(fun).tpe}") else throw Error(i"unexpected type.\n fun = $fun,\n methPart(fun) = ${methPart(fun)},\n methPart(fun).tpe = ${methPart(fun).tpe},\n tpe = ${fun.tpe}") From 77dbd3bb667cc7e279a6d1d303921bd4ae2ed48c Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 21 Nov 2022 13:56:12 +0100 Subject: [PATCH 03/14] Add a Context parameter to Message This will be the basis on which we modify contexts for printing diagnsotics. --- .../src/dotty/tools/dotc/core/Decorators.scala | 2 +- .../src/dotty/tools/dotc/core/TypeErrors.scala | 4 ++-- .../tools/dotc/parsing/xml/MarkupParsers.scala | 3 ++- .../dotty/tools/dotc/reporting/Message.scala | 4 ++-- .../dotty/tools/dotc/reporting/messages.scala | 18 +++++++++--------- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Decorators.scala b/compiler/src/dotty/tools/dotc/core/Decorators.scala index bbbdbc8f5eba..cc4319349008 100644 --- a/compiler/src/dotty/tools/dotc/core/Decorators.scala +++ b/compiler/src/dotty/tools/dotc/core/Decorators.scala @@ -59,7 +59,7 @@ object Decorators { end extension extension (str: => String) - def toMessage: Message = NoExplanation(str) + def toMessage: Message = NoExplanation(str)(using NoContext) /** Implements a findSymbol method on iterators of Symbols that * works like find but avoids Option, replacing None with NoSymbol. diff --git a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala index bab0e0c61e5f..b1885d3e9fba 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala @@ -16,7 +16,7 @@ import config.Printers.cyclicErrors abstract class TypeError(using creationContext: Context) extends Exception(""): - /** Convert to message. This uses an additional Context, so that we + /** Convert to message. This takes an additional Context, so that we * use the context when the message is first produced, i.e. when the TypeError * is handled. This makes a difference for CyclicErrors since we need to know * the context where the completed symbol is referenced, but the creation @@ -26,7 +26,7 @@ abstract class TypeError(using creationContext: Context) extends Exception(""): */ def toMessage(using Context): Message - /** Uses creation context to produce the message */ + /** Uses creationContext to produce the message */ override def getMessage: String = toMessage.message object TypeError: diff --git a/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala b/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala index 3d9f5fb7ad6d..c82d565de7cb 100644 --- a/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala @@ -6,6 +6,7 @@ package xml import scala.language.unsafeNulls import scala.collection.mutable +import core.Contexts.Context import mutable.{ Buffer, ArrayBuffer, ListBuffer } import scala.util.control.ControlThrowable import util.Chars.SU @@ -50,7 +51,7 @@ object MarkupParsers { override def getMessage: String = "input ended while parsing XML" } - class MarkupParser(parser: Parser, final val preserveWS: Boolean)(implicit src: SourceFile) extends MarkupParserCommon { + class MarkupParser(parser: Parser, final val preserveWS: Boolean)(using Context) extends MarkupParserCommon { import Tokens.{ LBRACE, RBRACE } diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index 9e397d606491..f2517551728e 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -40,7 +40,7 @@ object Message { * @param errorId a unique id identifying the message, this will be * used to reference documentation online */ -abstract class Message(val errorId: ErrorMessageID) { self => +abstract class Message(val errorId: ErrorMessageID)(using Context) { self => import Message._ /** The `msg` contains the diagnostic message e.g: @@ -144,7 +144,7 @@ abstract class Message(val errorId: ErrorMessageID) { self => } /** The fallback `Message` containing no explanation and having no `kind` */ -class NoExplanation(msgFn: => String) extends Message(ErrorMessageID.NoExplanationID) { +class NoExplanation(msgFn: => String)(using Context) extends Message(ErrorMessageID.NoExplanationID) { def msg: String = msgFn def explain: String = "" val kind: MessageKind = MessageKind.NoKind diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 94edf63e5487..71da8231e4df 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -40,10 +40,10 @@ import cc.CaptureSet.IdentityCaptRefMap * ``` */ - abstract class SyntaxMsg(errorId: ErrorMessageID) extends Message(errorId): + abstract class SyntaxMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): def kind = MessageKind.Syntax - abstract class TypeMsg(errorId: ErrorMessageID) extends Message(errorId): + abstract class TypeMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): def kind = MessageKind.Type trait ShowMatchTrace(tps: Type*)(using Context) extends Message: @@ -55,27 +55,27 @@ import cc.CaptureSet.IdentityCaptRefMap def explain = err.whyNoMatchStr(found, expected) override def canExplain = true - abstract class NamingMsg(errorId: ErrorMessageID) extends Message(errorId): + abstract class NamingMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): def kind = MessageKind.Naming - abstract class DeclarationMsg(errorId: ErrorMessageID) extends Message(errorId): + abstract class DeclarationMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): def kind = MessageKind.Declaration /** A simple not found message (either for idents, or member selection. * Messages of this class are sometimes dropped in favor of other, more * specific messages. */ - abstract class NotFoundMsg(errorId: ErrorMessageID) extends Message(errorId): + abstract class NotFoundMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): def kind = MessageKind.NotFound def name: Name - abstract class PatternMatchMsg(errorId: ErrorMessageID) extends Message(errorId): + abstract class PatternMatchMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): def kind = MessageKind.PatternMatch - abstract class CyclicMsg(errorId: ErrorMessageID) extends Message(errorId): + abstract class CyclicMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): def kind = MessageKind.Cyclic - abstract class ReferenceMsg(errorId: ErrorMessageID) extends Message(errorId): + abstract class ReferenceMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): def kind = MessageKind.Reference abstract class EmptyCatchOrFinallyBlock(tryBody: untpd.Tree, errNo: ErrorMessageID)(using Context) @@ -1097,7 +1097,7 @@ import cc.CaptureSet.IdentityCaptRefMap |""" } - class OverrideError(override val msg: String) extends DeclarationMsg(OverrideErrorID): + class OverrideError(override val msg: String)(using Context) extends DeclarationMsg(OverrideErrorID): def explain = "" class OverrideTypeMismatchError(override val msg: String, memberTp: Type, otherTp: Type)(using Context) From 94e9ba1b7745bc25d70d7ba5e7b15fcbee8765a2 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 21 Nov 2022 16:55:50 +0100 Subject: [PATCH 04/14] Turn more strings into messages In particular: The missingArg message of ImplicitSearchError becomes a regular Message now. --- .../dotty/tools/dotc/inlines/Inlines.scala | 3 +- .../tools/dotc/reporting/ErrorMessageID.scala | 2 + .../dotty/tools/dotc/reporting/Message.scala | 5 +- .../dotty/tools/dotc/reporting/messages.scala | 324 +++++++++++++++--- .../dotc/transform/PCPCheckAndHeal.scala | 8 +- .../dotty/tools/dotc/typer/Applications.scala | 14 +- .../src/dotty/tools/dotc/typer/Checking.scala | 4 +- .../tools/dotc/typer/ErrorReporting.scala | 208 +---------- .../dotty/tools/dotc/typer/Implicits.scala | 55 ++- .../src/dotty/tools/dotc/typer/Namer.scala | 2 +- .../dotty/tools/dotc/typer/TypeAssigner.scala | 15 +- compiler/test-resources/repl/i4184 | 2 +- .../reporting/UserDefinedErrorMessages.scala | 20 +- tests/neg-custom-args/i13838.check | 2 +- tests/neg-macros/i6436.check | 2 +- tests/neg-macros/i9014b.check | 2 +- .../jsconstructortag-error-in-typer.check | 6 +- tests/neg/i10098.check | 8 +- tests/neg/i10603a.check | 2 +- tests/neg/i10603b.check | 2 +- tests/neg/i11066.check | 2 +- tests/neg/i11797.check | 2 +- tests/neg/i11897.check | 10 +- tests/neg/i11982.check | 2 +- tests/neg/i12049.check | 2 +- tests/neg/i12232.check | 4 +- tests/neg/i12591.check | 2 +- tests/neg/i13846.check | 2 +- tests/neg/i13864.check | 2 +- tests/neg/i13991.check | 2 +- tests/neg/i14025.check | 4 +- tests/neg/i14127.check | 2 +- tests/neg/i14432.check | 2 +- tests/neg/i14432a.check | 2 +- tests/neg/i14432b.check | 2 +- tests/neg/i14432c.check | 4 +- tests/neg/i14432d.check | 2 +- tests/neg/i14823.check | 2 +- tests/neg/i14823a.check | 8 +- tests/neg/i15618.check | 2 +- tests/neg/i15998.check | 2 +- tests/neg/i4986a.check | 2 +- tests/neg/i4986c.check | 32 +- tests/neg/i5498-postfixOps.check | 2 +- tests/neg/i7613.check | 2 +- tests/neg/i7709.check | 16 +- tests/neg/i9185.check | 2 +- tests/neg/i9568.check | 2 +- tests/neg/i9958.check | 2 +- tests/neg/implicitSearch.check | 4 +- tests/neg/mirror-synthesis-errors-b.check | 16 +- tests/neg/mirror-synthesis-errors.check | 14 +- tests/neg/missing-implicit-2.check | 2 +- tests/neg/missing-implicit1.check | 4 +- tests/neg/missing-implicit2.check | 4 +- tests/neg/missing-implicit3.check | 2 +- tests/neg/missing-implicit4.check | 4 +- tests/neg/safeThrowsStrawman.check | 4 +- tests/neg/saferExceptions.check | 4 +- tests/neg/subtyping.check | 4 +- tests/neg/summon-function.check | 2 +- tests/neg/summonInline.check | 4 +- 62 files changed, 442 insertions(+), 434 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 1806cdfc909b..b5923154c088 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -442,8 +442,7 @@ object Inlines: val evidence = evTyper.inferImplicitArg(tpt.tpe, tpt.span) evidence.tpe match case fail: Implicits.SearchFailureType => - val msg = evTyper.missingArgMsg(evidence, tpt.tpe, "") - errorTree(call, em"$msg") + errorTree(call, evTyper.missingArgMsg(evidence, tpt.tpe, "")) case _ => evidence } diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index d9140a6309b8..f98c728e04b6 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -185,6 +185,8 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case TargetNameOnTopLevelClassID // errorNumber: 169 case NotClassTypeID // errorNumber 170 case MissingArgumentID // errorNumer 171 + case MissingImplicitArgumentID // errorNumber 172 + case CannotBeAccessedID // errorNumber 173 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index f2517551728e..205a60a236f5 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -133,6 +133,9 @@ abstract class Message(val errorId: ErrorMessageID)(using Context) { self => def explain = self.explain ++ suffix override def canExplain = true + def wrap(header: => String, trailer: => String): Message = + mapMsg(s => header ++ s ++ trailer) + /** Override with `true` for messages that should always be shown even if their * position overlaps another messsage of a different class. On the other hand * multiple messages of the same class with overlapping positions will lead @@ -144,7 +147,7 @@ abstract class Message(val errorId: ErrorMessageID)(using Context) { self => } /** The fallback `Message` containing no explanation and having no `kind` */ -class NoExplanation(msgFn: => String)(using Context) extends Message(ErrorMessageID.NoExplanationID) { +final class NoExplanation(msgFn: => String)(using Context) extends Message(ErrorMessageID.NoExplanationID) { def msg: String = msgFn def explain: String = "" val kind: MessageKind = MessageKind.NoKind diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 71da8231e4df..3a43e9b5c0b2 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -15,9 +15,10 @@ import printing.Formatting import ErrorMessageID._ import ast.Trees import config.{Feature, ScalaVersion} -import typer.ErrorReporting.{err, matchReductionAddendum} +import typer.ErrorReporting.{err, matchReductionAddendum, substitutableTypeSymbolsInScope} import typer.ProtoTypes.ViewProto -import typer.Implicits.Candidate +import typer.Implicits.* +import typer.Inferencing import scala.util.control.NonFatal import StdNames.nme import printing.Formatting.hl @@ -25,6 +26,8 @@ import ast.Trees._ import ast.untpd import ast.tpd import transform.SymUtils._ +import scala.util.matching.Regex +import java.util.regex.Matcher.quoteReplacement import cc.CaptureSet.IdentityCaptRefMap /** Messages @@ -244,34 +247,34 @@ import cc.CaptureSet.IdentityCaptRefMap class TypeMismatch(found: Type, expected: Type, inTree: Option[untpd.Tree], addenda: => String*)(using Context) extends TypeMismatchMsg(found, expected)(TypeMismatchID): - // replace constrained TypeParamRefs and their typevars by their bounds where possible - // and the bounds are not f-bounds. - // The idea is that if the bounds are also not-subtypes of each other to report - // the type mismatch on the bounds instead of the original TypeParamRefs, since - // these are usually easier to analyze. We exclude F-bounds since these would - // lead to a recursive infinite expansion. - object reported extends TypeMap, IdentityCaptRefMap: - def setVariance(v: Int) = variance = v - val constraint = mapCtx.typerState.constraint - var fbounded = false - def apply(tp: Type): Type = tp match - case tp: TypeParamRef => - constraint.entry(tp) match - case bounds: TypeBounds => - if variance < 0 then apply(TypeComparer.fullUpperBound(tp)) - else if variance > 0 then apply(TypeComparer.fullLowerBound(tp)) - else tp - case NoType => tp - case instType => apply(instType) - case tp: TypeVar => - apply(tp.stripTypeVar) - case tp: LazyRef => - fbounded = true - tp - case _ => - mapOver(tp) - def msg = + // replace constrained TypeParamRefs and their typevars by their bounds where possible + // and the bounds are not f-bounds. + // The idea is that if the bounds are also not-subtypes of each other to report + // the type mismatch on the bounds instead of the original TypeParamRefs, since + // these are usually easier to analyze. We exclude F-bounds since these would + // lead to a recursive infinite expansion. + object reported extends TypeMap, IdentityCaptRefMap: + def setVariance(v: Int) = variance = v + val constraint = mapCtx.typerState.constraint + var fbounded = false + def apply(tp: Type): Type = tp match + case tp: TypeParamRef => + constraint.entry(tp) match + case bounds: TypeBounds => + if variance < 0 then apply(TypeComparer.fullUpperBound(tp)) + else if variance > 0 then apply(TypeComparer.fullLowerBound(tp)) + else tp + case NoType => tp + case instType => apply(instType) + case tp: TypeVar => + apply(tp.stripTypeVar) + case tp: LazyRef => + fbounded = true + tp + case _ => + mapOver(tp) + val found1 = reported(found) reported.setVariance(-1) val expected1 = reported(expected) @@ -290,6 +293,7 @@ import cc.CaptureSet.IdentityCaptRefMap s"""|Found: $foundStr |Required: $expectedStr""".stripMargin + whereSuffix + postScript + end msg override def explain = val treeStr = inTree.map(x => s"\nTree: ${x.show}").getOrElse("") @@ -1126,7 +1130,7 @@ import cc.CaptureSet.IdentityCaptRefMap class ExpectedTokenButFound(expected: Token, found: Token)(using Context) extends SyntaxMsg(ExpectedTokenButFoundID) { - private lazy val foundText = Tokens.showToken(found) + private def foundText = Tokens.showToken(found) def msg = val expectedText = @@ -1248,10 +1252,10 @@ import cc.CaptureSet.IdentityCaptRefMap class SkolemInInferred(tree: tpd.Tree, pt: Type, argument: tpd.Tree)(using Context) extends TypeMsg(SkolemInInferredID): - private def argStr = - if argument.isEmpty then "" - else i" from argument of type ${argument.tpe.widen}" def msg = + def argStr = + if argument.isEmpty then "" + else i" from argument of type ${argument.tpe.widen}" e"""Failure to generate given instance for type $pt$argStr) | |I found: $tree @@ -1296,7 +1300,7 @@ import cc.CaptureSet.IdentityCaptRefMap * imported by * or defined in */ - private def bindingString(prec: BindingPrec, whereFound: Context, qualifier: String = "") = { + private def bindingString(prec: BindingPrec, whereFound: Context, qualifier: String = "")(using Context) = { val howVisible = prec match { case BindingPrec.Definition => "defined" case BindingPrec.Inheritance => "inherited" @@ -1332,7 +1336,7 @@ import cc.CaptureSet.IdentityCaptRefMap class MethodDoesNotTakeParameters(tree: tpd.Tree)(using Context) extends TypeMsg(MethodDoesNotTakeParametersId) { - def methodSymbol: Symbol = + def methodSymbol(using Context): Symbol = def recur(t: tpd.Tree): Symbol = val sym = tpd.methPart(t).symbol if sym == defn.Any_typeCast then @@ -1391,7 +1395,7 @@ import cc.CaptureSet.IdentityCaptRefMap class TypeDoesNotTakeParameters(tpe: Type, params: List[untpd.Tree])(using Context) extends TypeMsg(TypeDoesNotTakeParametersID) { - private def fboundsAddendum = + private def fboundsAddendum(using Context) = if tpe.typeSymbol.isAllOf(Provisional | TypeParam) then "\n(Note that F-bounds of type parameters may not be type lambdas)" else "" @@ -1531,11 +1535,10 @@ import cc.CaptureSet.IdentityCaptRefMap implicit ctx: Context) extends SyntaxMsg(OnlyClassesCanHaveDeclaredButUndefinedMembersID) { - private def varNote = - if (sym.is(Mutable)) "Note that variables need to be initialized to be defined." - else "" def msg = e"""Declaration of $sym not allowed here: only classes can have declared but undefined members""" - def explain = s"$varNote" + def explain = + if sym.is(Mutable) then "Note that variables need to be initialized to be defined." + else "" } class CannotExtendAnyVal(sym: Symbol)(using Context) @@ -1569,7 +1572,7 @@ import cc.CaptureSet.IdentityCaptRefMap class CannotHaveSameNameAs(sym: Symbol, cls: Symbol, reason: CannotHaveSameNameAs.Reason)(using Context) extends SyntaxMsg(CannotHaveSameNameAsID) { import CannotHaveSameNameAs._ - def reasonMessage: String = reason match { + def reasonMessage(using Context): String = reason match { case CannotBeOverridden => "class definitions cannot be overridden" case DefinedInSelf(self) => s"""cannot define ${sym.showKind} member with the same name as a ${cls.showKind} member in self reference ${self.name}. @@ -1851,16 +1854,16 @@ import cc.CaptureSet.IdentityCaptRefMap } class AlreadyDefined(name: Name, owner: Symbol, conflicting: Symbol)(using Context) extends NamingMsg(AlreadyDefinedID): - private def where: String = - if conflicting.effectiveOwner.is(Package) && conflicting.associatedFile != null then - i" in ${conflicting.associatedFile}" - else if conflicting.owner == owner then "" - else i" in ${conflicting.owner}" - private def note = - if owner.is(Method) || conflicting.is(Method) then - "\n\nNote that overloaded methods must all be defined in the same group of toplevel definitions" - else "" def msg = + def where: String = + if conflicting.effectiveOwner.is(Package) && conflicting.associatedFile != null then + i" in ${conflicting.associatedFile}" + else if conflicting.owner == owner then "" + else i" in ${conflicting.owner}" + def note = + if owner.is(Method) || conflicting.is(Method) then + "\n\nNote that overloaded methods must all be defined in the same group of toplevel definitions" + else "" if conflicting.isTerm != name.isTermName then e"$name clashes with $conflicting$where; the two must be defined together" else @@ -1868,12 +1871,12 @@ import cc.CaptureSet.IdentityCaptRefMap def explain = "" class PackageNameAlreadyDefined(pkg: Symbol)(using Context) extends NamingMsg(PackageNameAlreadyDefinedID) { - lazy val (where, or) = - if pkg.associatedFile == null then ("", "") - else (s" in ${pkg.associatedFile}", " or delete the containing class file") - def msg = e"""${pkg.name} is the name of $pkg$where. - |It cannot be used at the same time as the name of a package.""" + def msg = + def where = if pkg.associatedFile == null then "" else s" in ${pkg.associatedFile}" + e"""${pkg.name} is the name of $pkg$where. + |It cannot be used at the same time as the name of a package.""" def explain = + def or = if pkg.associatedFile == null then "" else " or delete the containing class file" e"""An ${hl("object")} or other toplevel definition cannot have the same name as an existing ${hl("package")}. |Rename either one of them$or.""" } @@ -2487,7 +2490,7 @@ import cc.CaptureSet.IdentityCaptRefMap class ImplicitSearchTooLargeWarning(limit: Int, openSearchPairs: List[(Candidate, Type)])(using Context) extends TypeMsg(ImplicitSearchTooLargeID): override def showAlways = true - def showQuery(query: (Candidate, Type)): String = + def showQuery(query: (Candidate, Type))(using Context): String = i" ${query._1.ref.symbol.showLocated} for ${query._2}}" def msg = e"""Implicit search problem too large. @@ -2533,3 +2536,212 @@ import cc.CaptureSet.IdentityCaptRefMap def msg = ex"$tp is not a class type" def explain = "" + class MissingImplicitArgument( + arg: tpd.Tree, + pt: Type, + where: String, + paramSymWithMethodCallTree: Option[(Symbol, tpd.Tree)] = None, + ignoredInstanceNormalImport: => Option[SearchSuccess], + importSuggestionAddendum: Type => String + )(using ctx: Context) extends TypeMsg(MissingImplicitArgumentID): + + def msg: String = + + def formatMsg(shortForm: String)(headline: String = shortForm) = arg match + case arg: Trees.SearchFailureIdent[?] => + arg.tpe match + case _: NoMatchingImplicits => headline + case tpe: SearchFailureType => + i"$headline. ${tpe.explanation}" + case _ => headline + case _ => + arg.tpe match + case tpe: SearchFailureType => + val original = arg match + case Inlined(call, _, _) => call + case _ => arg + i"""$headline. + |I found: + | + | ${original.show.replace("\n", "\n ")} + | + |But ${tpe.explanation}.""" + case _ => headline + + /** Format `raw` implicitNotFound or implicitAmbiguous argument, replacing + * all occurrences of `${X}` where `X` is in `paramNames` with the + * corresponding shown type in `args`. + */ + def userDefinedErrorString(raw: String, paramNames: List[String], args: List[Type]): String = { + def translate(name: String): Option[String] = { + val idx = paramNames.indexOf(name) + if (idx >= 0) Some(ex"${args(idx)}") else None + } + + """\$\{\s*([^}\s]+)\s*\}""".r.replaceAllIn(raw, (_: Regex.Match) match { + case Regex.Groups(v) => quoteReplacement(translate(v).getOrElse("")).nn + }) + } + + /** Extract a user defined error message from a symbol `sym` + * with an annotation matching the given class symbol `cls`. + */ + def userDefinedMsg(sym: Symbol, cls: Symbol) = for { + ann <- sym.getAnnotation(cls) + msg <- ann.argumentConstantString(0) + } yield msg + + def location(preposition: String) = if (where.isEmpty) "" else s" $preposition $where" + + def defaultAmbiguousImplicitMsg(ambi: AmbiguousImplicits) = + s"Ambiguous given instances: ${ambi.explanation}${location("of")}" + + def defaultImplicitNotFoundMessage = + ex"No given instance of type $pt was found${location("for")}" + + /** Construct a custom error message given an ambiguous implicit + * candidate `alt` and a user defined message `raw`. + */ + def userDefinedAmbiguousImplicitMsg(alt: SearchSuccess, raw: String) = { + val params = alt.ref.underlying match { + case p: PolyType => p.paramNames.map(_.toString) + case _ => Nil + } + def resolveTypes(targs: List[tpd.Tree])(using Context) = + targs.map(a => Inferencing.fullyDefinedType(a.tpe, "type argument", a.srcPos)) + + // We can extract type arguments from: + // - a function call: + // @implicitAmbiguous("msg A=${A}") + // implicit def f[A](): String = ... + // implicitly[String] // found: f[Any]() + // + // - an eta-expanded function: + // @implicitAmbiguous("msg A=${A}") + // implicit def f[A](x: Int): String = ... + // implicitly[Int => String] // found: x => f[Any](x) + + val call = tpd.closureBody(alt.tree) // the tree itself if not a closure + val targs = tpd.typeArgss(call).flatten + val args = resolveTypes(targs)(using ctx.fresh.setTyperState(alt.tstate)) + userDefinedErrorString(raw, params, args) + } + + /** @param rawMsg Message template with variables, e.g. "Variable A is ${A}" + * @param sym Symbol of the annotated type or of the method whose parameter was annotated + * @param substituteType Function substituting specific types for abstract types associated with variables, e.g A -> Int + */ + def formatAnnotationMessage(rawMsg: String, sym: Symbol, substituteType: Type => Type): String = { + val substitutableTypesSymbols = substitutableTypeSymbolsInScope(sym) + + userDefinedErrorString( + rawMsg, + paramNames = substitutableTypesSymbols.map(_.name.unexpandedName.toString), + args = substitutableTypesSymbols.map(_.typeRef).map(substituteType) + ) + } + + /** Extracting the message from a method parameter, e.g. in + * + * trait Foo + * + * def foo(implicit @annotation.implicitNotFound("Foo is missing") foo: Foo): Any = ??? + */ + def userDefinedImplicitNotFoundParamMessage: Option[String] = paramSymWithMethodCallTree.flatMap { (sym, applTree) => + userDefinedMsg(sym, defn.ImplicitNotFoundAnnot).map { rawMsg => + val fn = tpd.funPart(applTree) + val targs = tpd.typeArgss(applTree).flatten + val methodOwner = fn.symbol.owner + val methodOwnerType = tpd.qualifier(fn).tpe + val methodTypeParams = fn.symbol.paramSymss.flatten.filter(_.isType) + val methodTypeArgs = targs.map(_.tpe) + val substituteType = (_: Type).asSeenFrom(methodOwnerType, methodOwner).subst(methodTypeParams, methodTypeArgs) + formatAnnotationMessage(rawMsg, sym.owner, substituteType) + } + } + + /** Extracting the message from a type, e.g. in + * + * @annotation.implicitNotFound("Foo is missing") + * trait Foo + * + * def foo(implicit foo: Foo): Any = ??? + */ + def userDefinedImplicitNotFoundTypeMessage: Option[String] = + def recur(tp: Type): Option[String] = tp match + case tp: TypeRef => + val sym = tp.symbol + userDefinedImplicitNotFoundTypeMessageFor(sym).orElse(recur(tp.info)) + case tp: ClassInfo => + tp.baseClasses.iterator + .map(userDefinedImplicitNotFoundTypeMessageFor) + .find(_.isDefined).flatten + case tp: TypeProxy => + recur(tp.superType) + case tp: AndType => + recur(tp.tp1).orElse(recur(tp.tp2)) + case _ => + None + recur(pt) + + def userDefinedImplicitNotFoundTypeMessageFor(sym: Symbol): Option[String] = + for + rawMsg <- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot) + if Feature.migrateTo3 || sym != defn.Function1 + // Don't inherit "No implicit view available..." message if subtypes of Function1 are not treated as implicit conversions anymore + yield + val substituteType = (_: Type).asSeenFrom(pt, sym) + formatAnnotationMessage(rawMsg, sym, substituteType) + + def hiddenImplicitsAddendum: String = + def hiddenImplicitNote(s: SearchSuccess) = + i"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." + val normalImports = ignoredInstanceNormalImport.map(hiddenImplicitNote) + normalImports.getOrElse(importSuggestionAddendum(pt)) + + object AmbiguousImplicitMsg { + def unapply(search: SearchSuccess): Option[String] = + userDefinedMsg(search.ref.symbol, defn.ImplicitAmbiguousAnnot) + } + + arg.tpe match + case ambi: AmbiguousImplicits => + (ambi.alt1, ambi.alt2) match + case (alt @ AmbiguousImplicitMsg(msg), _) => + userDefinedAmbiguousImplicitMsg(alt, msg) + case (_, alt @ AmbiguousImplicitMsg(msg)) => + userDefinedAmbiguousImplicitMsg(alt, msg) + case _ => + defaultAmbiguousImplicitMsg(ambi) + case ambi @ TooUnspecific(target) => + ex"""No implicit search was attempted${location("for")} + |since the expected type $target is not specific enough""" + case _ => + val shortMessage = userDefinedImplicitNotFoundParamMessage + .orElse(userDefinedImplicitNotFoundTypeMessage) + .getOrElse(defaultImplicitNotFoundMessage) + formatMsg(shortMessage)() + ++ hiddenImplicitsAddendum + ++ matchReductionAddendum(pt) + end msg + def explain = "" + end MissingImplicitArgument + + class CannotBeAccessed(tpe: NamedType, superAccess: Boolean)(using Context) + extends ReferenceMsg(CannotBeAccessedID): + def msg = + val pre = tpe.prefix + val name = tpe.name + val alts = tpe.denot.alternatives.map(_.symbol).filter(_.exists) + val whatCanNot = alts match + case Nil => + e"$name cannot" + case sym :: Nil => + e"${if (sym.owner == pre.typeSymbol) sym.show else sym.showLocated} cannot" + case _ => + e"none of the overloaded alternatives named $name can" + val where = if (ctx.owner.exists) s" from ${ctx.owner.enclosingClass}" else "" + val whyNot = new StringBuffer + alts.foreach(_.isAccessibleFrom(pre, superAccess, whyNot)) + e"$whatCanNot be accessed as a member of $pre$where.$whyNot" + def explain = "" diff --git a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala index 263b0040eb24..3b83a9bf1cd5 100644 --- a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala @@ -246,10 +246,10 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( checkStable(tp, pos, "type witness") getQuoteTypeTags.getTagRef(tp) case _: SearchFailureType => - report.error(i"""Reference to $tp within quotes requires a given $reqType in scope. - |${ctx.typer.missingArgMsg(tag, reqType, "")} - | - |""", pos) + report.error( + ctx.typer.missingArgMsg(tag, reqType, "").wrap( + i"Reference to $tp within quotes requires a given $reqType in scope.\n", "\n"), + pos) tp case _ => report.error(i"""Reference to $tp within quotes requires a given $reqType in scope. diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index b3828fc074bd..94cd39b89bcd 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -478,7 +478,7 @@ trait Applications extends Compatibility { matchArgs(orderedArgs, methType.paramInfos, 0) case _ => if (methType.isError) ok = false - else fail(s"$methString does not take parameters".toMessage) + else fail(em"$methString does not take parameters") } /** The application was successful */ @@ -518,10 +518,10 @@ trait Applications extends Compatibility { else { // name not (or no longer) available for named arg def msg = if (methodType.paramNames contains aname) - s"parameter $aname of $methString is already instantiated" + em"parameter $aname of $methString is already instantiated" else - s"$methString does not have a parameter $aname" - fail(msg.toMessage, arg.asInstanceOf[Arg]) + em"$methString does not have a parameter $aname" + fail(msg, arg.asInstanceOf[Arg]) arg :: handleNamed(pnamesRest, args1, nameToArg, toDrop) } case arg :: args1 => @@ -647,10 +647,10 @@ trait Applications extends Compatibility { def msg = arg match case untpd.Tuple(Nil) if applyKind == ApplyKind.InfixTuple && funType.widen.isNullaryMethod => - i"can't supply unit value with infix notation because nullary $methString takes no arguments; use dotted invocation instead: (...).${methRef.name}()" + em"can't supply unit value with infix notation because nullary $methString takes no arguments; use dotted invocation instead: (...).${methRef.name}()" case _ => - i"too many arguments for $methString" - fail(msg.toMessage, arg) + em"too many arguments for $methString" + fail(msg, arg) case nil => } } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index e7d9dee8998c..54ab1a8e0501 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -843,9 +843,9 @@ trait Checking { case Select(id, _) => id case _ => EmptyTree if extractor.isEmpty then - e"pattern binding uses refutable extractor" + i"pattern binding uses refutable extractor" else - e"pattern binding uses refutable extractor `$extractor`" + i"pattern binding uses refutable extractor `$extractor`" val fix = if isPatDef then "adding `: @unchecked` after the expression" diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 6fa1e3b07e4a..fe2becfd0907 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -10,11 +10,9 @@ import Trees._ import NameOps._ import util.SrcPos import config.Feature -import java.util.regex.Matcher.quoteReplacement import reporting._ import collection.mutable -import scala.util.matching.Regex object ErrorReporting { @@ -87,18 +85,18 @@ object ErrorReporting { def expectedTypeStr(tp: Type): String = tp match { case tp: PolyProto => - e"type arguments [${tp.targs.tpes}%, %] and ${expectedTypeStr(revealDeepenedArgs(tp.resultType))}" + i"type arguments [${tp.targs.tpes}%, %] and ${expectedTypeStr(revealDeepenedArgs(tp.resultType))}" case tp: FunProto => def argStr(tp: FunProto): String = val result = revealDeepenedArgs(tp.resultType) match { case restp: FunProto => argStr(restp) case _: WildcardType | _: IgnoredProto => "" - case tp => e" and expected result type $tp" + case tp => i" and expected result type $tp" } - e"(${tp.typedArgs().tpes}%, %)$result" + i"(${tp.typedArgs().tpes}%, %)$result" s"arguments ${argStr(tp)}" case _ => - e"expected type $tp" + i"expected type $tp" } def anonymousTypeMemberStr(tpe: Type): String = { @@ -107,12 +105,12 @@ object ErrorReporting { case _: MethodOrPoly => "method" case _ => "value of type" } - e"$kind $tpe" + i"$kind $tpe" } def overloadedAltsStr(alts: List[SingleDenotation]): String = - e"overloaded alternatives of ${denotStr(alts.head)} with types\n" + - e" ${alts map (_.info)}%\n %" + i"""overloaded alternatives of ${denotStr(alts.head)} with types + | ${alts map (_.info)}%\n %""" def denotStr(denot: Denotation): String = if (denot.isOverloaded) overloadedAltsStr(denot.alternatives) @@ -263,195 +261,3 @@ object ErrorReporting { def err(using Context): Errors = new Errors } - -class ImplicitSearchError( - arg: tpd.Tree, - pt: Type, - where: String, - paramSymWithMethodCallTree: Option[(Symbol, tpd.Tree)] = None, - ignoredInstanceNormalImport: => Option[SearchSuccess], - importSuggestionAddendum: => String -)(using ctx: Context) { - - def missingArgMsg = arg.tpe match { - case ambi: AmbiguousImplicits => - (ambi.alt1, ambi.alt2) match { - case (alt @ AmbiguousImplicitMsg(msg), _) => - userDefinedAmbiguousImplicitMsg(alt, msg) - case (_, alt @ AmbiguousImplicitMsg(msg)) => - userDefinedAmbiguousImplicitMsg(alt, msg) - case _ => - defaultAmbiguousImplicitMsg(ambi) - } - case ambi @ TooUnspecific(target) => - ex"""No implicit search was attempted${location("for")} - |since the expected type $target is not specific enough""" - case _ => - val shortMessage = userDefinedImplicitNotFoundParamMessage - .orElse(userDefinedImplicitNotFoundTypeMessage) - .getOrElse(defaultImplicitNotFoundMessage) - formatMsg(shortMessage)() - ++ hiddenImplicitsAddendum - ++ ErrorReporting.matchReductionAddendum(pt) - } - - private def formatMsg(shortForm: String)(headline: String = shortForm) = arg match - case arg: Trees.SearchFailureIdent[?] => - arg.tpe match - case _: NoMatchingImplicits => headline - case tpe: SearchFailureType => - i"$headline. ${tpe.explanation}" - case _ => headline - case _ => - arg.tpe match - case tpe: SearchFailureType => - val original = arg match - case Inlined(call, _, _) => call - case _ => arg - i"""$headline. - |I found: - | - | ${original.show.replace("\n", "\n ")} - | - |But ${tpe.explanation}.""" - case _ => headline - - /** Format `raw` implicitNotFound or implicitAmbiguous argument, replacing - * all occurrences of `${X}` where `X` is in `paramNames` with the - * corresponding shown type in `args`. - */ - private def userDefinedErrorString(raw: String, paramNames: List[String], args: List[Type]): String = { - def translate(name: String): Option[String] = { - val idx = paramNames.indexOf(name) - if (idx >= 0) Some(ex"${args(idx)}") else None - } - - """\$\{\s*([^}\s]+)\s*\}""".r.replaceAllIn(raw, (_: Regex.Match) match { - case Regex.Groups(v) => quoteReplacement(translate(v).getOrElse("")).nn - }) - } - - /** Extract a user defined error message from a symbol `sym` - * with an annotation matching the given class symbol `cls`. - */ - private def userDefinedMsg(sym: Symbol, cls: Symbol) = for { - ann <- sym.getAnnotation(cls) - msg <- ann.argumentConstantString(0) - } yield msg - - private def location(preposition: String) = if (where.isEmpty) "" else s" $preposition $where" - - private def defaultAmbiguousImplicitMsg(ambi: AmbiguousImplicits) = - s"Ambiguous given instances: ${ambi.explanation}${location("of")}" - - private def defaultImplicitNotFoundMessage = - ex"No given instance of type $pt was found${location("for")}" - - /** Construct a custom error message given an ambiguous implicit - * candidate `alt` and a user defined message `raw`. - */ - private def userDefinedAmbiguousImplicitMsg(alt: SearchSuccess, raw: String) = { - val params = alt.ref.underlying match { - case p: PolyType => p.paramNames.map(_.toString) - case _ => Nil - } - def resolveTypes(targs: List[tpd.Tree])(using Context) = - targs.map(a => Inferencing.fullyDefinedType(a.tpe, "type argument", a.srcPos)) - - // We can extract type arguments from: - // - a function call: - // @implicitAmbiguous("msg A=${A}") - // implicit def f[A](): String = ... - // implicitly[String] // found: f[Any]() - // - // - an eta-expanded function: - // @implicitAmbiguous("msg A=${A}") - // implicit def f[A](x: Int): String = ... - // implicitly[Int => String] // found: x => f[Any](x) - - val call = tpd.closureBody(alt.tree) // the tree itself if not a closure - val targs = tpd.typeArgss(call).flatten - val args = resolveTypes(targs)(using ctx.fresh.setTyperState(alt.tstate)) - userDefinedErrorString(raw, params, args) - } - - /** @param rawMsg Message template with variables, e.g. "Variable A is ${A}" - * @param sym Symbol of the annotated type or of the method whose parameter was annotated - * @param substituteType Function substituting specific types for abstract types associated with variables, e.g A -> Int - */ - private def formatAnnotationMessage(rawMsg: String, sym: Symbol, substituteType: Type => Type): String = { - val substitutableTypesSymbols = ErrorReporting.substitutableTypeSymbolsInScope(sym) - - userDefinedErrorString( - rawMsg, - paramNames = substitutableTypesSymbols.map(_.name.unexpandedName.toString), - args = substitutableTypesSymbols.map(_.typeRef).map(substituteType) - ) - } - - /** Extracting the message from a method parameter, e.g. in - * - * trait Foo - * - * def foo(implicit @annotation.implicitNotFound("Foo is missing") foo: Foo): Any = ??? - */ - private def userDefinedImplicitNotFoundParamMessage: Option[String] = paramSymWithMethodCallTree.flatMap { (sym, applTree) => - userDefinedMsg(sym, defn.ImplicitNotFoundAnnot).map { rawMsg => - val fn = tpd.funPart(applTree) - val targs = tpd.typeArgss(applTree).flatten - val methodOwner = fn.symbol.owner - val methodOwnerType = tpd.qualifier(fn).tpe - val methodTypeParams = fn.symbol.paramSymss.flatten.filter(_.isType) - val methodTypeArgs = targs.map(_.tpe) - val substituteType = (_: Type).asSeenFrom(methodOwnerType, methodOwner).subst(methodTypeParams, methodTypeArgs) - formatAnnotationMessage(rawMsg, sym.owner, substituteType) - } - } - - /** Extracting the message from a type, e.g. in - * - * @annotation.implicitNotFound("Foo is missing") - * trait Foo - * - * def foo(implicit foo: Foo): Any = ??? - */ - private def userDefinedImplicitNotFoundTypeMessage: Option[String] = - def recur(tp: Type): Option[String] = tp match - case tp: TypeRef => - val sym = tp.symbol - userDefinedImplicitNotFoundTypeMessage(sym).orElse(recur(tp.info)) - case tp: ClassInfo => - tp.baseClasses.iterator - .map(userDefinedImplicitNotFoundTypeMessage) - .find(_.isDefined).flatten - case tp: TypeProxy => - recur(tp.superType) - case tp: AndType => - recur(tp.tp1).orElse(recur(tp.tp2)) - case _ => - None - recur(pt) - - private def userDefinedImplicitNotFoundTypeMessage(sym: Symbol): Option[String] = - for - rawMsg <- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot) - if Feature.migrateTo3 || sym != defn.Function1 - // Don't inherit "No implicit view available..." message if subtypes of Function1 are not treated as implicit conversions anymore - yield - val substituteType = (_: Type).asSeenFrom(pt, sym) - formatAnnotationMessage(rawMsg, sym, substituteType) - - private def hiddenImplicitsAddendum: String = - def hiddenImplicitNote(s: SearchSuccess) = - e"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." - - val normalImports = ignoredInstanceNormalImport.map(hiddenImplicitNote) - - normalImports.getOrElse(importSuggestionAddendum) - end hiddenImplicitsAddendum - - private object AmbiguousImplicitMsg { - def unapply(search: SearchSuccess): Option[String] = - userDefinedMsg(search.ref.symbol, defn.ImplicitAmbiguousAnnot) - } -} diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 1229ba2dcc33..0f04dc60c06b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -435,19 +435,17 @@ object Implicits: final protected def qualify(using Context): String = expectedType match { case SelectionProto(name, mproto, _, _) if !argument.isEmpty => - e"provide an extension method `$name` on ${argument.tpe}" + i"provide an extension method `$name` on ${argument.tpe}" case NoType => - if (argument.isEmpty) e"match expected type" - else e"convert from ${argument.tpe} to expected type" + if (argument.isEmpty) i"match expected type" + else i"convert from ${argument.tpe} to expected type" case _ => - if (argument.isEmpty) e"match type ${clarify(expectedType)}" - else e"convert from ${argument.tpe} to ${clarify(expectedType)}" + if (argument.isEmpty) i"match type ${clarify(expectedType)}" + else i"convert from ${argument.tpe} to ${clarify(expectedType)}" } /** An explanation of the cause of the failure as a string */ - def explanation(using Context): String - - def msg(using Context): Message = explanation.toMessage + def explanation(using Context): String = msg.message /** If search was for an implicit conversion, a note describing the failure * in more detail - this is either empty or starts with a '\n' @@ -488,8 +486,9 @@ object Implicits: map(tp) } - def explanation(using Context): String = - e"no implicit values were found that $qualify" + def msg(using Context): Message = + em"no implicit values were found that $qualify" + override def toString = s"NoMatchingImplicits($expectedType, $argument)" } @@ -509,20 +508,20 @@ object Implicits: i""" |Note that implicit conversions were not tried because the result of an implicit conversion |must be more specific than $target""" - override def explanation(using Context) = - i"""${super.explanation}. - |The expected type $target is not specific enough, so no search was attempted""" + + override def msg(using Context) = + super.msg.wrap("", "\nThe expected type $target is not specific enough, so no search was attempted") override def toString = s"TooUnspecific" /** An ambiguous implicits failure */ class AmbiguousImplicits(val alt1: SearchSuccess, val alt2: SearchSuccess, val expectedType: Type, val argument: Tree) extends SearchFailureType { - def explanation(using Context): String = + def msg(using Context): Message = var str1 = err.refStr(alt1.ref) var str2 = err.refStr(alt2.ref) if str1 == str2 then str1 = ctx.printer.toTextRef(alt1.ref).show str2 = ctx.printer.toTextRef(alt2.ref).show - e"both $str1 and $str2 $qualify" + em"both $str1 and $str2 $qualify" override def whyNoConversion(using Context): String = if !argument.isEmpty && argument.tpe.widen.isRef(defn.NothingClass) then "" @@ -536,21 +535,21 @@ object Implicits: class MismatchedImplicit(ref: TermRef, val expectedType: Type, val argument: Tree) extends SearchFailureType { - def explanation(using Context): String = - e"${err.refStr(ref)} does not $qualify" + def msg(using Context): Message = + em"${err.refStr(ref)} does not $qualify" } class DivergingImplicit(ref: TermRef, val expectedType: Type, val argument: Tree) extends SearchFailureType { - def explanation(using Context): String = - e"${err.refStr(ref)} produces a diverging implicit search when trying to $qualify" + def msg(using Context): Message = + em"${err.refStr(ref)} produces a diverging implicit search when trying to $qualify" } /** A search failure type for attempted ill-typed extension method calls */ class FailedExtension(extApp: Tree, val expectedType: Type, val whyFailed: Message) extends SearchFailureType: def argument = EmptyTree - def explanation(using Context) = e"$extApp does not $qualify" + def msg(using Context) = em"$extApp does not $qualify" /** A search failure type for aborted searches of extension methods, typically * because of a cyclic reference or similar. @@ -558,7 +557,6 @@ object Implicits: class NestedFailure(_msg: Message, val expectedType: Type) extends SearchFailureType: def argument = EmptyTree override def msg(using Context) = _msg - def explanation(using Context) = msg.toString /** A search failure type for failed synthesis of terms for special types */ class SynthesisFailure(reasons: List[String], val expectedType: Type) extends SearchFailureType: @@ -570,7 +568,7 @@ object Implicits: else reasons.mkString(" ", "", "") - def explanation(using Context) = e"Failed to synthesize an instance of type ${clarify(expectedType)}:${formatReasons}" + def msg(using Context) = em"Failed to synthesize an instance of type ${clarify(expectedType)}:${formatReasons}" end Implicits @@ -905,7 +903,7 @@ trait Implicits: pt: Type, where: String, paramSymWithMethodCallTree: Option[(Symbol, Tree)] = None - )(using Context): String = { + )(using Context): Message = { def findHiddenImplicitsCtx(c: Context): Context = if c == NoContext then c else c.freshOver(findHiddenImplicitsCtx(c.outer)).addMode(Mode.FindHiddenImplicits) @@ -928,8 +926,7 @@ trait Implicits: // example where searching for a nested type causes an infinite loop. None - val error = new ImplicitSearchError(arg, pt, where, paramSymWithMethodCallTree, ignoredInstanceNormalImport, importSuggestionAddendum(pt)) - error.missingArgMsg + MissingImplicitArgument(arg, pt, where, paramSymWithMethodCallTree, ignoredInstanceNormalImport, importSuggestionAddendum) } /** A string indicating the formal parameter corresponding to a missing argument */ @@ -939,9 +936,9 @@ trait Implicits: val qt = qual.tpe.widen val qt1 = qt.dealiasKeepAnnots def addendum = if (qt1 eq qt) "" else (i"\nThe required type is an alias of: $qt1") - e"parameter of ${qual.tpe.widen}$addendum" + i"parameter of ${qual.tpe.widen}$addendum" case _ => - e"${ if paramName.is(EvidenceParamName) then "an implicit parameter" + i"${ if paramName.is(EvidenceParamName) then "an implicit parameter" else s"parameter $paramName" } of $methodStr" } @@ -1050,7 +1047,9 @@ trait Implicits: withMode(Mode.OldOverloadingResolution)(inferImplicit(pt, argument, span)) match { case altResult: SearchSuccess => report.migrationWarning( - s"According to new implicit resolution rules, this will be ambiguous:\n${result.reason.explanation}", + result.reason.msg.wrap( + s"According to new implicit resolution rules, this will be ambiguous:\n", + ""), ctx.source.atSpan(span)) altResult case _ => diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index a9570fe9ebc1..204c2217f3be 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -462,7 +462,7 @@ class Namer { typer: Typer => if isProvisional then typr.println(i"provisional superclass $first for $cls") first = AnnotatedType(first, Annotation(defn.ProvisionalSuperClassAnnot)) - checkFeasibleParent(first, cls.srcPos, e" in inferred superclass $first") :: parents + checkFeasibleParent(first, cls.srcPos, i" in inferred superclass $first") :: parents end ensureFirstIsClass /** Add child annotation for `child` to annotations of `cls`. The annotation diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index fb14b242beac..ad6e75a9b8c8 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -172,21 +172,8 @@ trait TypeAssigner { errorType(msg, tree.srcPos) def inaccessibleErrorType(tpe: NamedType, superAccess: Boolean, pos: SrcPos)(using Context): Type = - val pre = tpe.prefix - val name = tpe.name - val alts = tpe.denot.alternatives.map(_.symbol).filter(_.exists) - val whatCanNot = alts match - case Nil => - e"$name cannot" - case sym :: Nil => - e"${if (sym.owner == pre.typeSymbol) sym.show else sym.showLocated} cannot" - case _ => - e"none of the overloaded alternatives named $name can" - val where = if (ctx.owner.exists) s" from ${ctx.owner.enclosingClass}" else "" - val whyNot = new StringBuffer - alts.foreach(_.isAccessibleFrom(pre, superAccess, whyNot)) if tpe.isError then tpe - else errorType(e"$whatCanNot be accessed as a member of $pre$where.$whyNot", pos) + else errorType(CannotBeAccessed(tpe, superAccess), pos) def processAppliedType(tree: untpd.Tree, tp: Type)(using Context): Type = tp match case AppliedType(tycon, args) => diff --git a/compiler/test-resources/repl/i4184 b/compiler/test-resources/repl/i4184 index 2c4eb7d12a6f..1e57c3e5ad80 100644 --- a/compiler/test-resources/repl/i4184 +++ b/compiler/test-resources/repl/i4184 @@ -5,7 +5,7 @@ scala> object bar { class Foo } scala> implicit def eqFoo: CanEqual[foo.Foo, foo.Foo] = CanEqual.derived def eqFoo: CanEqual[foo.Foo, foo.Foo] scala> object Bar { new foo.Foo == new bar.Foo } --- Error: ---------------------------------------------------------------------- +-- [E172] Type Error: ---------------------------------------------------------- 1 | object Bar { new foo.Foo == new bar.Foo } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | Values of types foo.Foo and bar.Foo cannot be compared with == or != diff --git a/compiler/test/dotty/tools/dotc/reporting/UserDefinedErrorMessages.scala b/compiler/test/dotty/tools/dotc/reporting/UserDefinedErrorMessages.scala index 4d73b0d88b55..807d3a19f8f3 100644 --- a/compiler/test/dotty/tools/dotc/reporting/UserDefinedErrorMessages.scala +++ b/compiler/test/dotty/tools/dotc/reporting/UserDefinedErrorMessages.scala @@ -26,9 +26,9 @@ class UserDefinedErrorMessages extends ErrorMessagesTest { given Context = itcx assertMessageCount(1, messages) - val (m: NoExplanation) :: Nil = messages: @unchecked + val (m: TypeMsg) :: Nil = messages: @unchecked - assertEquals(m.msg, "Could not prove Int =!= Int") + assertEquals(m.message, "Could not prove Int =!= Int") } @Test def userDefinedImplicitAmbiguous2 = @@ -50,9 +50,9 @@ class UserDefinedErrorMessages extends ErrorMessagesTest { given Context = itcx assertMessageCount(1, messages) - val (m: NoExplanation) :: Nil = messages: @unchecked + val (m: TypeMsg) :: Nil = messages: @unchecked - assertEquals(m.msg, "Could not prove Int =!= Int") + assertEquals(m.message, "Could not prove Int =!= Int") } @Test def userDefinedImplicitAmbiguous3 = @@ -75,9 +75,9 @@ class UserDefinedErrorMessages extends ErrorMessagesTest { given Context = itcx assertMessageCount(1, messages) - val (m: NoExplanation) :: Nil = messages: @unchecked + val (m: TypeMsg) :: Nil = messages: @unchecked - assertEquals(m.msg, "Could not prove Int =!= Int") + assertEquals(m.message, "Could not prove Int =!= Int") } @Test def userDefinedImplicitAmbiguous4 = @@ -97,9 +97,9 @@ class UserDefinedErrorMessages extends ErrorMessagesTest { given Context = itcx assertMessageCount(1, messages) - val (m: NoExplanation) :: Nil = messages: @unchecked + val (m: TypeMsg) :: Nil = messages: @unchecked - assertEquals(m.msg, "msg A=Any") + assertEquals(m.message, "msg A=Any") } @Test def userDefinedImplicitAmbiguous5 = @@ -119,8 +119,8 @@ class UserDefinedErrorMessages extends ErrorMessagesTest { given Context = itcx assertMessageCount(1, messages) - val (m: NoExplanation) :: Nil = messages: @unchecked + val (m: TypeMsg) :: Nil = messages: @unchecked - assertEquals(m.msg, "msg A=Any") + assertEquals(m.message, "msg A=Any") } } diff --git a/tests/neg-custom-args/i13838.check b/tests/neg-custom-args/i13838.check index 2c93e4001461..db4be5f021af 100644 --- a/tests/neg-custom-args/i13838.check +++ b/tests/neg-custom-args/i13838.check @@ -1,4 +1,4 @@ --- Error: tests/neg-custom-args/i13838.scala:10:5 ---------------------------------------------------------------------- +-- [E172] Type Error: tests/neg-custom-args/i13838.scala:10:5 ---------------------------------------------------------- 10 | foo // error | ^ |No given instance of type Order[X] was found for parameter x$1 of method foo in object FooT diff --git a/tests/neg-macros/i6436.check b/tests/neg-macros/i6436.check index 9c422fa11b99..43e93b2e64e5 100644 --- a/tests/neg-macros/i6436.check +++ b/tests/neg-macros/i6436.check @@ -1,4 +1,4 @@ --- Error: tests/neg-macros/i6436.scala:5:9 ----------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg-macros/i6436.scala:5:9 ----------------------------------------------------------------- 5 | case '{ StringContext(${Varargs(parts)}*) } => // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | No given instance of type scala.quoted.Quotes was found diff --git a/tests/neg-macros/i9014b.check b/tests/neg-macros/i9014b.check index 0d972e123a30..de0be2d5c1fa 100644 --- a/tests/neg-macros/i9014b.check +++ b/tests/neg-macros/i9014b.check @@ -1,5 +1,5 @@ --- Error: tests/neg-macros/i9014b/Test_2.scala:1:23 -------------------------------------------------------------------- +-- [E172] Type Error: tests/neg-macros/i9014b/Test_2.scala:1:23 -------------------------------------------------------- 1 |val tests = summon[Bar] // error | ^ | No given instance of type Bar was found for parameter x of method summon in object Predef. diff --git a/tests/neg-scalajs/jsconstructortag-error-in-typer.check b/tests/neg-scalajs/jsconstructortag-error-in-typer.check index ba845de39231..888fa163e81c 100644 --- a/tests/neg-scalajs/jsconstructortag-error-in-typer.check +++ b/tests/neg-scalajs/jsconstructortag-error-in-typer.check @@ -1,4 +1,4 @@ --- Error: tests/neg-scalajs/jsconstructortag-error-in-typer.scala:9:39 ------------------------------------------------- +-- [E172] Type Error: tests/neg-scalajs/jsconstructortag-error-in-typer.scala:9:39 ------------------------------------- 9 | val a = js.constructorTag[ScalaClass] // error | ^ |No given instance of type scala.scalajs.js.ConstructorTag[ScalaClass] was found for parameter tag of method constructorTag in package scala.scalajs.js. @@ -7,7 +7,7 @@ | scala.scalajs.js.ConstructorTag.materialize[T] | |But method materialize in object ConstructorTag does not match type scala.scalajs.js.ConstructorTag[ScalaClass]. --- Error: tests/neg-scalajs/jsconstructortag-error-in-typer.scala:10:39 ------------------------------------------------ +-- [E172] Type Error: tests/neg-scalajs/jsconstructortag-error-in-typer.scala:10:39 ------------------------------------ 10 | val b = js.constructorTag[ScalaTrait] // error | ^ |No given instance of type scala.scalajs.js.ConstructorTag[ScalaTrait] was found for parameter tag of method constructorTag in package scala.scalajs.js. @@ -16,7 +16,7 @@ | scala.scalajs.js.ConstructorTag.materialize[T] | |But method materialize in object ConstructorTag does not match type scala.scalajs.js.ConstructorTag[ScalaTrait]. --- Error: tests/neg-scalajs/jsconstructortag-error-in-typer.scala:11:45 ------------------------------------------------ +-- [E172] Type Error: tests/neg-scalajs/jsconstructortag-error-in-typer.scala:11:45 ------------------------------------ 11 | val c = js.constructorTag[ScalaObject.type] // error | ^ |No given instance of type scala.scalajs.js.ConstructorTag[ScalaObject.type] was found for parameter tag of method constructorTag in package scala.scalajs.js. diff --git a/tests/neg/i10098.check b/tests/neg/i10098.check index 06d0c62b69c0..94cc911b7753 100644 --- a/tests/neg/i10098.check +++ b/tests/neg/i10098.check @@ -1,16 +1,16 @@ --- Error: tests/neg/i10098.scala:20:32 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i10098.scala:20:32 --------------------------------------------------------------------- 20 | implicitly[Bar12[Int, String]] // error | ^ | There's no Foo2[String, Int] --- Error: tests/neg/i10098.scala:21:32 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i10098.scala:21:32 --------------------------------------------------------------------- 21 | implicitly[Bar21[Int, String]] // error | ^ | There's no Foo1[String, Int] --- Error: tests/neg/i10098.scala:22:32 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i10098.scala:22:32 --------------------------------------------------------------------- 22 | implicitly[Baz12[Int, String]] // error | ^ | There's no Baz12[Int, String] --- Error: tests/neg/i10098.scala:23:32 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i10098.scala:23:32 --------------------------------------------------------------------- 23 | implicitly[Baz21[Int, String]] // error | ^ | There's no Baz21[Int, String] diff --git a/tests/neg/i10603a.check b/tests/neg/i10603a.check index 578b942f6023..1d885dfdb762 100644 --- a/tests/neg/i10603a.check +++ b/tests/neg/i10603a.check @@ -1,4 +1,4 @@ --- Error: tests/neg/i10603a.scala:2:35 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i10603a.scala:2:35 --------------------------------------------------------------------- 2 | val x = implicitly[List[Boolean]] // error | ^ | No given instance of type List[Boolean] was found for parameter e of method implicitly in object Predef diff --git a/tests/neg/i10603b.check b/tests/neg/i10603b.check index 14a03fc9d3d7..cd230c44538b 100644 --- a/tests/neg/i10603b.check +++ b/tests/neg/i10603b.check @@ -1,4 +1,4 @@ --- Error: tests/neg/i10603b.scala:4:35 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i10603b.scala:4:35 --------------------------------------------------------------------- 4 | val x = implicitly[List[Boolean]] // error | ^ | No implicit view available from Int => Boolean. diff --git a/tests/neg/i11066.check b/tests/neg/i11066.check index a73cb5566439..24b91b2f69ee 100644 --- a/tests/neg/i11066.check +++ b/tests/neg/i11066.check @@ -1,4 +1,4 @@ --- Error: tests/neg/i11066.scala:15:37 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i11066.scala:15:37 --------------------------------------------------------------------- 15 |val x = Greeter.greet("Who's there?") // error | ^ |Ambiguous given instances: both given instance joesPrompt in object JoesPrefs and given instance jillsPrompt in object JillsPrefs match type PreferredPrompt of parameter prompt of method greet in object Greeter diff --git a/tests/neg/i11797.check b/tests/neg/i11797.check index 80090b6b2faf..62b8a8828069 100644 --- a/tests/neg/i11797.check +++ b/tests/neg/i11797.check @@ -1,4 +1,4 @@ --- Error: tests/neg/i11797.scala:6:17 ---------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i11797.scala:6:17 ---------------------------------------------------------------------- 6 | summon[Foo.Bar] // error | ^ | Oops diff --git a/tests/neg/i11897.check b/tests/neg/i11897.check index 4b001fadc606..67de6dbab37d 100644 --- a/tests/neg/i11897.check +++ b/tests/neg/i11897.check @@ -23,23 +23,23 @@ | ^^^^^^^^^^^ | given patterns are not allowed in a val definition, | please bind to an identifier and use an alias given. --- Error: tests/neg/i11897.scala:16:18 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i11897.scala:16:18 --------------------------------------------------------------------- 16 | assert(summon[A] == A(23)) // error | ^ | No given instance of type A was found for parameter x of method summon in object Predef --- Error: tests/neg/i11897.scala:17:18 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i11897.scala:17:18 --------------------------------------------------------------------- 17 | assert(summon[B] == B(false)) // error | ^ | No given instance of type B was found for parameter x of method summon in object Predef --- Error: tests/neg/i11897.scala:18:18 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i11897.scala:18:18 --------------------------------------------------------------------- 18 | assert(summon[C] == C("c")) // error | ^ | No given instance of type C was found for parameter x of method summon in object Predef --- Error: tests/neg/i11897.scala:19:18 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i11897.scala:19:18 --------------------------------------------------------------------- 19 | assert(summon[E] == E(93)) // error | ^ | No given instance of type E was found for parameter x of method summon in object Predef --- Error: tests/neg/i11897.scala:20:18 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i11897.scala:20:18 --------------------------------------------------------------------- 20 | assert(summon[G] == G(101)) // error | ^ | No given instance of type G was found for parameter x of method summon in object Predef diff --git a/tests/neg/i11982.check b/tests/neg/i11982.check index 48ec252a4410..304accbf0269 100644 --- a/tests/neg/i11982.check +++ b/tests/neg/i11982.check @@ -1,4 +1,4 @@ --- Error: tests/neg/i11982.scala:22:38 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i11982.scala:22:38 --------------------------------------------------------------------- 22 | val p1: ("msg", 42) = unpair[Tshape] // error: no singleton value for Any | ^ |No singleton value available for Any; eligible singleton types for `ValueOf` synthesis include literals and stable paths. diff --git a/tests/neg/i12049.check b/tests/neg/i12049.check index edf76a0823b9..a58624ec6778 100644 --- a/tests/neg/i12049.check +++ b/tests/neg/i12049.check @@ -31,7 +31,7 @@ | | case t1 *: t2 *: ts => Tuple.Concat[Reverse[ts], (t2, t1)] | case EmptyTuple => EmptyTuple --- Error: tests/neg/i12049.scala:24:20 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i12049.scala:24:20 --------------------------------------------------------------------- 24 |val _ = summon[M[B]] // error | ^ | No given instance of type M[B] was found for parameter x of method summon in object Predef diff --git a/tests/neg/i12232.check b/tests/neg/i12232.check index 8de9b4317a31..eb4875ab77c3 100644 --- a/tests/neg/i12232.check +++ b/tests/neg/i12232.check @@ -1,10 +1,10 @@ --- Error: tests/neg/i12232.scala:17:15 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i12232.scala:17:15 --------------------------------------------------------------------- 17 | foo(min(3, 4)) // error: works in Scala 2, not in 3 | ^ | No given instance of type Op[Int, Int, V] was found for parameter op of method min in object Foo | | where: V is a type variable with constraint <: Double --- Error: tests/neg/i12232.scala:19:16 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i12232.scala:19:16 --------------------------------------------------------------------- 19 | foo(minR(3, 4)) // error: works in Scala 2, not in 3 | ^ | No given instance of type Op[Int, Int, R] was found for parameter op of method minR in object Foo diff --git a/tests/neg/i12591.check b/tests/neg/i12591.check index e050038659b5..17d418713aa2 100644 --- a/tests/neg/i12591.check +++ b/tests/neg/i12591.check @@ -1,4 +1,4 @@ --- Error: tests/neg/i12591/Inner.scala:12:31 --------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i12591/Inner.scala:12:31 --------------------------------------------------------------- 12 |val badSummon = summon[TC[Bar]] // error here | ^ |Ambiguous given instances: both outer.inner.Foo.ofFoo and outer.Foo.ofFoo match type outer.inner.Foo.TC[outer.Bar] of parameter x of method summon in object Predef diff --git a/tests/neg/i13846.check b/tests/neg/i13846.check index 69ea0f0e51ac..a57db35ef6dd 100644 --- a/tests/neg/i13846.check +++ b/tests/neg/i13846.check @@ -2,7 +2,7 @@ 3 |def foo(): Int throws ArithmeticException = 1 / 0 // error | ^^^^^^^^^^^^^^^^^^^ | throws clause cannot be defined for RuntimeException --- Error: tests/neg/i13846.scala:7:9 ----------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i13846.scala:7:9 ----------------------------------------------------------------------- 7 | foo() // error | ^ | The capability to throw exception ArithmeticException is missing. diff --git a/tests/neg/i13864.check b/tests/neg/i13864.check index 54e81ea82774..6020ff8c6086 100644 --- a/tests/neg/i13864.check +++ b/tests/neg/i13864.check @@ -3,7 +3,7 @@ | ^^^^^^^^^^ | Implementation restriction: cannot generate CanThrow capability for this kind of catch. | CanThrow capabilities can only be generated for cases of the form `ex: T` where `T` is fully defined. --- Error: tests/neg/i13864.scala:9:10 ---------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i13864.scala:9:10 ---------------------------------------------------------------------- 9 | foo(1) // error | ^ | The capability to throw exception Ex[Int] is missing. diff --git a/tests/neg/i13991.check b/tests/neg/i13991.check index bd1cda58c046..4c24e14a85c6 100644 --- a/tests/neg/i13991.check +++ b/tests/neg/i13991.check @@ -1,4 +1,4 @@ --- Error: tests/neg/i13991.scala:5:7 ----------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i13991.scala:5:7 ----------------------------------------------------------------------- 5 | first[String] // error // before line 10 to test alignment of the error message `|` | ^^^^^^^^^^^^^ | No given instance of type Foo[String] was found diff --git a/tests/neg/i14025.check b/tests/neg/i14025.check index 804d45523deb..a44cdc67c1f8 100644 --- a/tests/neg/i14025.check +++ b/tests/neg/i14025.check @@ -1,8 +1,8 @@ --- Error: tests/neg/i14025.scala:1:88 ---------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i14025.scala:1:88 ---------------------------------------------------------------------- 1 |val foo = summon[deriving.Mirror.Product { type MirroredType = [X] =>> [Y] =>> (X, Y) }] // error | ^ |No given instance of type deriving.Mirror.Product{type MirroredType[X] = [Y] =>> (X, Y)} was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Product{type MirroredType[X] = [Y] =>> (X, Y)}: type `[X] =>> [Y] =>> (X, Y)` is not a generic product because its subpart `[X] =>> [Y] =>> (X, Y)` is not a supported kind (either `*` or `* -> *`) --- Error: tests/neg/i14025.scala:2:90 ---------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i14025.scala:2:90 ---------------------------------------------------------------------- 2 |val bar = summon[deriving.Mirror.Sum { type MirroredType = [X] =>> [Y] =>> List[(X, Y)] }] // error | ^ |No given instance of type deriving.Mirror.Sum{type MirroredType[X] = [Y] =>> List[(X, Y)]} was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Sum{type MirroredType[X] = [Y] =>> List[(X, Y)]}: type `[X] =>> [Y] =>> List[(X, Y)]` is not a generic sum because its subpart `[X] =>> [Y] =>> List[(X, Y)]` is not a supported kind (either `*` or `* -> *`) diff --git a/tests/neg/i14127.check b/tests/neg/i14127.check index d00c44d39078..15babe8b2775 100644 --- a/tests/neg/i14127.check +++ b/tests/neg/i14127.check @@ -1,4 +1,4 @@ --- Error: tests/neg/i14127.scala:6:55 ---------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i14127.scala:6:55 ---------------------------------------------------------------------- 6 | *: Int *: Int *: Int *: Int *: Int *: EmptyTuple)]] // error | ^ |No given instance of type deriving.Mirror.Of[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, diff --git a/tests/neg/i14432.check b/tests/neg/i14432.check index 424d43bb119e..d19d952b0153 100644 --- a/tests/neg/i14432.check +++ b/tests/neg/i14432.check @@ -1,4 +1,4 @@ --- Error: tests/neg/i14432.scala:13:33 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i14432.scala:13:33 --------------------------------------------------------------------- 13 |val mFoo = summon[Mirror.Of[Foo]] // error: no mirror found | ^ |No given instance of type deriving.Mirror.Of[example.Foo] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[example.Foo]: diff --git a/tests/neg/i14432a.check b/tests/neg/i14432a.check index 4c975789507c..705a7ed0e88b 100644 --- a/tests/neg/i14432a.check +++ b/tests/neg/i14432a.check @@ -1,4 +1,4 @@ --- Error: tests/neg/i14432a.scala:14:43 -------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i14432a.scala:14:43 -------------------------------------------------------------------- 14 | val mFoo = summon[Mirror.Of[example.Foo]] // error: no mirror found | ^ |No given instance of type deriving.Mirror.Of[example.Foo] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[example.Foo]: diff --git a/tests/neg/i14432b.check b/tests/neg/i14432b.check index 1859beedd781..5b0dac3e6ad0 100644 --- a/tests/neg/i14432b.check +++ b/tests/neg/i14432b.check @@ -1,4 +1,4 @@ --- Error: tests/neg/i14432b.scala:15:43 -------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i14432b.scala:15:43 -------------------------------------------------------------------- 15 | val mFoo = summon[Mirror.Of[example.Foo]] // error: no mirror found | ^ |No given instance of type deriving.Mirror.Of[example.Foo] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[example.Foo]: diff --git a/tests/neg/i14432c.check b/tests/neg/i14432c.check index e675656ad77f..a61e100ceb98 100644 --- a/tests/neg/i14432c.check +++ b/tests/neg/i14432c.check @@ -1,8 +1,8 @@ --- Error: tests/neg/i14432c.scala:12:18 -------------------------------------------------------------------------------- +-- [E173] Reference Error: tests/neg/i14432c.scala:12:18 --------------------------------------------------------------- 12 |class Bar extends example.Foo(23) { // error: cant access private[example] ctor | ^^^^^^^^^^^ | constructor Foo cannot be accessed as a member of example.Foo from class Bar. --- Error: tests/neg/i14432c.scala:16:43 -------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i14432c.scala:16:43 -------------------------------------------------------------------- 16 | val mFoo = summon[Mirror.Of[example.Foo]] // error: no mirror | ^ |No given instance of type deriving.Mirror.Of[example.Foo] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[example.Foo]: diff --git a/tests/neg/i14432d.check b/tests/neg/i14432d.check index 664c3f2073da..aff070d90192 100644 --- a/tests/neg/i14432d.check +++ b/tests/neg/i14432d.check @@ -1,4 +1,4 @@ --- Error: tests/neg/i14432d.scala:17:45 -------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i14432d.scala:17:45 -------------------------------------------------------------------- 17 | val mFoo = summon[Mirror.Of[example.Foo]] // error | ^ |No given instance of type deriving.Mirror.Of[example.Foo] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[example.Foo]: diff --git a/tests/neg/i14823.check b/tests/neg/i14823.check index b4662d60519c..47b15f04e2da 100644 --- a/tests/neg/i14823.check +++ b/tests/neg/i14823.check @@ -1,4 +1,4 @@ --- Error: tests/neg/i14823.scala:8:50 ---------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i14823.scala:8:50 ---------------------------------------------------------------------- 8 |val baz = summon[Mirror.Of[SubA[Int] | SubB[Int]]] // error | ^ |No given instance of type deriving.Mirror.Of[SubA[Int] | SubB[Int]] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[SubA[Int] | SubB[Int]]: diff --git a/tests/neg/i14823a.check b/tests/neg/i14823a.check index 644780067995..3c9b749780e0 100644 --- a/tests/neg/i14823a.check +++ b/tests/neg/i14823a.check @@ -1,22 +1,22 @@ --- Error: tests/neg/i14823a.scala:16:48 -------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i14823a.scala:16:48 -------------------------------------------------------------------- 16 |val foo = summon[Mirror.Of[Box[Int] | Box[Int]]] // error | ^ |No given instance of type deriving.Mirror.Of[Box[Int] | Box[Int]] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[Box[Int] | Box[Int]]: | * type `Box[Int] | Box[Int]` is not a generic product because its subpart `Box[Int] | Box[Int]` is a top-level union type. | * type `Box[Int] | Box[Int]` is not a generic sum because its subpart `Box[Int] | Box[Int]` is a top-level union type. --- Error: tests/neg/i14823a.scala:17:58 -------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i14823a.scala:17:58 -------------------------------------------------------------------- 17 |val bar = summon[MirrorK1.Of[[X] =>> Box[Int] | Box[Int]]] // error | ^ |No given instance of type MirrorK1.Of[[X] =>> Box[Int] | Box[Int]] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type MirrorK1.Of[[X] =>> Box[Int] | Box[Int]]: | * type `[A] =>> Box[Int] | Box[Int]` is not a generic product because its subpart `Box[Int] | Box[Int]` is a top-level union type. | * type `[A] =>> Box[Int] | Box[Int]` is not a generic sum because its subpart `Box[Int] | Box[Int]` is a top-level union type. --- Error: tests/neg/i14823a.scala:18:63 -------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i14823a.scala:18:63 -------------------------------------------------------------------- 18 |def baz = summon[deriving.Mirror.Of[Foo[String] | Foo[String]]] // error | ^ |No given instance of type deriving.Mirror.Of[Foo[String] | Foo[String]] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[Foo[String] | Foo[String]]: | * type `Foo[String] | Foo[String]` is not a generic product because its subpart `Foo[String] | Foo[String]` is a top-level union type. | * type `Foo[String] | Foo[String]` is not a generic sum because its subpart `Foo[String] | Foo[String]` is a top-level union type. --- Error: tests/neg/i14823a.scala:20:66 -------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i14823a.scala:20:66 -------------------------------------------------------------------- 20 |def qux = summon[deriving.Mirror.Of[Option[Int] | Option[String]]] // error | ^ |No given instance of type deriving.Mirror.Of[Option[Int] | Option[String]] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[Option[Int] | Option[String]]: diff --git a/tests/neg/i15618.check b/tests/neg/i15618.check index 0853da26c27a..91f557b12dcf 100644 --- a/tests/neg/i15618.check +++ b/tests/neg/i15618.check @@ -1,4 +1,4 @@ --- Error: tests/neg/i15618.scala:17:44 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i15618.scala:17:44 --------------------------------------------------------------------- 17 | def toArray: Array[ScalaType[T]] = Array() // error | ^ | No ClassTag available for ScalaType[T] diff --git a/tests/neg/i15998.check b/tests/neg/i15998.check index c745c7a84309..1f25946624cf 100644 --- a/tests/neg/i15998.check +++ b/tests/neg/i15998.check @@ -11,7 +11,7 @@ | must be more specific than CC[A] | | longer explanation available when compiling with `-explain` --- Error: tests/neg/i15998.scala:11:11 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i15998.scala:11:11 --------------------------------------------------------------------- 11 |val _ = bar // error | ^ | No implicit search was attempted for parameter x of method bar diff --git a/tests/neg/i4986a.check b/tests/neg/i4986a.check index 3aac0a7b2cf3..141f3fa8aacb 100644 --- a/tests/neg/i4986a.check +++ b/tests/neg/i4986a.check @@ -1,4 +1,4 @@ --- Error: tests/neg/i4986a.scala:6:57 ---------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i4986a.scala:6:57 ---------------------------------------------------------------------- 6 | def foo(l: Lst[Int]) = l.map[Int, List[String]](x => 1) // error | ^ |Cannot construct a collection of type List[String] with elements of type Int based on a collection of type List[Int].. diff --git a/tests/neg/i4986c.check b/tests/neg/i4986c.check index 30f746e5af7f..8befc30f5a60 100644 --- a/tests/neg/i4986c.check +++ b/tests/neg/i4986c.check @@ -1,64 +1,64 @@ --- Error: tests/neg/i4986c.scala:38:8 ---------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i4986c.scala:38:8 ---------------------------------------------------------------------- 38 | test.f // error | ^ | Missing X$Y for Test[Char] --- Error: tests/neg/i4986c.scala:39:13 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i4986c.scala:39:13 --------------------------------------------------------------------- 39 | test.g[Int] // error | ^ | Missing Outer[Int] with OuterMember = pkg.Outer[Int]#OuterMember --- Error: tests/neg/i4986c.scala:40:13 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i4986c.scala:40:13 --------------------------------------------------------------------- 40 | test.h[X$Y] // error | ^ | Missing Outer[pkg.X$Y] with OuterMember = pkg.Outer[pkg.X$Y]#OuterMember --- Error: tests/neg/i4986c.scala:41:24 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i4986c.scala:41:24 --------------------------------------------------------------------- 41 | test.i[Option[String]] // error | ^ | Missing implicit outer param of type Outer[Option[String]] for Test[Char] --- Error: tests/neg/i4986c.scala:42:43 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i4986c.scala:42:43 --------------------------------------------------------------------- 42 | test.j[(Long, Long), Int | String, Array] // error | ^ |Missing Inner[Int | String, Array] with InnerMember = pkg.Outer[(Long, Long)]#Inner[Int | String, Array]#InnerMember from Outer[(Long, Long)] with OuterMember = pkg.Outer[(Long, Long)]#OuterMember --- Error: tests/neg/i4986c.scala:43:53 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i4986c.scala:43:53 --------------------------------------------------------------------- 43 | test.k[Either[String, Any], Seq[Seq[Char]], Vector] // error | ^ | Missing implicit inner param of type Outer[Either[String, Any]]#Inner[Seq[Seq[Char]], Vector] for Test[Char] --- Error: tests/neg/i4986c.scala:45:87 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i4986c.scala:45:87 --------------------------------------------------------------------- 45 | implicitly[Outer[Option[String] | List[Iterable[Char]]] { type MyType = BigDecimal }] // error | ^ |Missing Outer[Option[String] | List[Iterable[Char]]] with OuterMember = pkg.Outer[Option[String] | List[Iterable[Char]]]{type MyType = BigDecimal}#OuterMember --- Error: tests/neg/i4986c.scala:46:106 -------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i4986c.scala:46:106 -------------------------------------------------------------------- 46 | implicitly[(Outer[Option[String] | List[Iterable[Char]]] { type MyType = BigDecimal })#Inner[Byte, Seq]] // error | ^ |Missing Inner[Byte, Seq] with InnerMember = pkg.Outer[Option[String] | List[Iterable[Char]]]{type MyType = BigDecimal}#Inner[Byte, Seq]#InnerMember from Outer[Option[String] | List[Iterable[Char]]] with OuterMember = pkg.Outer[Option[String] | List[Iterable[Char]]]{type MyType = BigDecimal}#OuterMember --- Error: tests/neg/i4986c.scala:47:33 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i4986c.scala:47:33 --------------------------------------------------------------------- 47 | implicitly[Outer[Int] @myAnnot] // error | ^ | Missing Outer[Int] with OuterMember = pkg.Outer[Int] @myAnnot#OuterMember --- Error: tests/neg/i4986c.scala:52:52 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i4986c.scala:52:52 --------------------------------------------------------------------- 52 | implicitly[Outer[Int] { type OuterMember = Long }] // error | ^ | Missing Outer[Int] with OuterMember = Long --- Error: tests/neg/i4986c.scala:53:24 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i4986c.scala:53:24 --------------------------------------------------------------------- 53 | implicitly[outer.type] // error | ^ | Missing Outer[Int] with OuterMember = pkg.Test.outer.OuterMember --- Error: tests/neg/i4986c.scala:54:104 -------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i4986c.scala:54:104 -------------------------------------------------------------------- 54 | implicitly[(Outer[Int] { type OuterMember = Long })#Inner[Long, Iterator] { type InnerMember = Byte }] // error | ^ | Missing Inner[Long, Iterator] with InnerMember = Byte from Outer[Int] with OuterMember = Long --- Error: tests/neg/i4986c.scala:55:69 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i4986c.scala:55:69 --------------------------------------------------------------------- 55 | implicitly[outer.Inner[Long, Iterator] { type InnerMember = Byte }] // error | ^ |Missing Inner[Long, Iterator] with InnerMember = Byte from Outer[Int] with OuterMember = pkg.Test.outer.OuterMember --- Error: tests/neg/i4986c.scala:56:24 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i4986c.scala:56:24 --------------------------------------------------------------------- 56 | implicitly[inner.type] // error | ^ |Missing Inner[Long, Iterator] with InnerMember = pkg.Test.inner.InnerMember from Outer[Int] with OuterMember = pkg.Test.outer.OuterMember --- Error: tests/neg/i4986c.scala:58:33 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i4986c.scala:58:33 --------------------------------------------------------------------- 58 | implicitly[U[Int, Option, Map]] // error | ^ | There's no U[Int, Option, Map] --- Error: tests/neg/i4986c.scala:62:19 --------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i4986c.scala:62:19 --------------------------------------------------------------------- 62 | i.m[Option[Long]] // error | ^ | String; List; [A, _] =>> List[Option[?]]; Int; Option[Long]; diff --git a/tests/neg/i5498-postfixOps.check b/tests/neg/i5498-postfixOps.check index 59568a7fd9f3..d41862364270 100644 --- a/tests/neg/i5498-postfixOps.check +++ b/tests/neg/i5498-postfixOps.check @@ -10,7 +10,7 @@ | expression expected but ')' found | | longer explanation available when compiling with `-explain` --- Error: tests/neg/i5498-postfixOps.scala:6:0 ------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i5498-postfixOps.scala:6:0 ------------------------------------------------------------- 6 | Seq(1, 2).filter(List(1,2) contains) // error: usage of postfix operator // error |^ |No given instance of type scala.concurrent.duration.DurationConversions.Classifier[Null] was found for parameter ev of method second in trait DurationConversions diff --git a/tests/neg/i7613.check b/tests/neg/i7613.check index 85d73b5c88f3..8ce12426c90c 100644 --- a/tests/neg/i7613.check +++ b/tests/neg/i7613.check @@ -1,4 +1,4 @@ --- Error: tests/neg/i7613.scala:10:16 ---------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i7613.scala:10:16 ---------------------------------------------------------------------- 10 | new BazLaws[A] {} // error | ^ | No given instance of type Baz[A] was found for parameter x$1 of constructor BazLaws in trait BazLaws diff --git a/tests/neg/i7709.check b/tests/neg/i7709.check index 20ecc4adce5f..180cf1939d16 100644 --- a/tests/neg/i7709.check +++ b/tests/neg/i7709.check @@ -1,46 +1,46 @@ --- Error: tests/neg/i7709.scala:5:20 ----------------------------------------------------------------------------------- +-- [E173] Reference Error: tests/neg/i7709.scala:5:20 ------------------------------------------------------------------ 5 | class B extends X.Y // error | ^^^ | class Y cannot be accessed as a member of X.type from class B. | Access to protected class Y not permitted because enclosing object A | is not a subclass of object X where target is defined --- Error: tests/neg/i7709.scala:6:21 ----------------------------------------------------------------------------------- +-- [E173] Reference Error: tests/neg/i7709.scala:6:21 ------------------------------------------------------------------ 6 | class B2 extends X.Y: // error | ^^^ | class Y cannot be accessed as a member of X.type from class B2. | Access to protected class Y not permitted because enclosing object A | is not a subclass of object X where target is defined --- Error: tests/neg/i7709.scala:9:28 ----------------------------------------------------------------------------------- +-- [E173] Reference Error: tests/neg/i7709.scala:9:28 ------------------------------------------------------------------ 9 | class B4 extends B3(new X.Y) // error | ^^^ | class Y cannot be accessed as a member of X.type from class B4. | Access to protected class Y not permitted because enclosing object A | is not a subclass of object X where target is defined --- Error: tests/neg/i7709.scala:11:34 ---------------------------------------------------------------------------------- +-- [E173] Reference Error: tests/neg/i7709.scala:11:34 ----------------------------------------------------------------- 11 | def this(n: Int) = this(new X.Y().toString) // error | ^^^ | class Y cannot be accessed as a member of X.type from class B5. | Access to protected class Y not permitted because enclosing object A | is not a subclass of object X where target is defined --- Error: tests/neg/i7709.scala:13:20 ---------------------------------------------------------------------------------- +-- [E173] Reference Error: tests/neg/i7709.scala:13:20 ----------------------------------------------------------------- 13 | class B extends X.Y // error | ^^^ | class Y cannot be accessed as a member of X.type from class B. | Access to protected class Y not permitted because enclosing trait T | is not a subclass of object X where target is defined --- Error: tests/neg/i7709.scala:18:18 ---------------------------------------------------------------------------------- +-- [E173] Reference Error: tests/neg/i7709.scala:18:18 ----------------------------------------------------------------- 18 | def y = new xx.Y // error | ^^^^ | class Y cannot be accessed as a member of XX from class C. | Access to protected class Y not permitted because enclosing class C | is not a subclass of class XX where target is defined --- Error: tests/neg/i7709.scala:23:20 ---------------------------------------------------------------------------------- +-- [E173] Reference Error: tests/neg/i7709.scala:23:20 ----------------------------------------------------------------- 23 | def y = new xx.Y // error | ^^^^ | class Y cannot be accessed as a member of XX from class D. | Access to protected class Y not permitted because enclosing class D | is not a subclass of class XX where target is defined --- Error: tests/neg/i7709.scala:31:20 ---------------------------------------------------------------------------------- +-- [E173] Reference Error: tests/neg/i7709.scala:31:20 ----------------------------------------------------------------- 31 | class Q extends X.Y // error | ^^^ | class Y cannot be accessed as a member of p.X.type from class Q. diff --git a/tests/neg/i9185.check b/tests/neg/i9185.check index 22751a3095ae..1616e739c473 100644 --- a/tests/neg/i9185.check +++ b/tests/neg/i9185.check @@ -10,7 +10,7 @@ | failed with: | | Ambiguous given instances: both object listMonad in object M and object optionMonad in object M match type M[F] of parameter m of method pure in object M --- Error: tests/neg/i9185.scala:8:28 ----------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i9185.scala:8:28 ----------------------------------------------------------------------- 8 | val value3 = M.pure("ola") // error | ^ |Ambiguous given instances: both object listMonad in object M and object optionMonad in object M match type M[F] of parameter m of method pure in object M diff --git a/tests/neg/i9568.check b/tests/neg/i9568.check index b6e20bdaf1be..3d03a6f1db63 100644 --- a/tests/neg/i9568.check +++ b/tests/neg/i9568.check @@ -1,4 +1,4 @@ --- Error: tests/neg/i9568.scala:13:10 ---------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i9568.scala:13:10 ---------------------------------------------------------------------- 13 | blaMonad.foo(bla) // error: diverges | ^ | No given instance of type => Monad[F] was found for parameter ev of method blaMonad in object Test diff --git a/tests/neg/i9958.check b/tests/neg/i9958.check index d8b37b996ec1..3f65286eebc2 100644 --- a/tests/neg/i9958.check +++ b/tests/neg/i9958.check @@ -1,4 +1,4 @@ --- Error: tests/neg/i9958.scala:1:30 ----------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i9958.scala:1:30 ----------------------------------------------------------------------- 1 |val x = summon[[X] =>> (X, X)] // error | ^ | No given instance of type [X] =>> (X, X) was found for parameter x of method summon in object Predef diff --git a/tests/neg/implicitSearch.check b/tests/neg/implicitSearch.check index d7ea6c01801c..e8efc744ac0a 100644 --- a/tests/neg/implicitSearch.check +++ b/tests/neg/implicitSearch.check @@ -1,4 +1,4 @@ --- Error: tests/neg/implicitSearch.scala:13:12 ------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/implicitSearch.scala:13:12 ------------------------------------------------------------- 13 | sort(xs) // error (with a partially constructed implicit argument shown) | ^ | No given instance of type Test.Ord[List[List[T]]] was found for parameter o of method sort in object Test. @@ -7,7 +7,7 @@ | Test.listOrd[List[T]](Test.listOrd[T](/* missing */summon[Test.Ord[T]])) | | But no implicit values were found that match type Test.Ord[T]. --- Error: tests/neg/implicitSearch.scala:15:38 ------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/implicitSearch.scala:15:38 ------------------------------------------------------------- 15 | listOrd(listOrd(implicitly[Ord[T]] /*not found*/)) // error | ^ | No given instance of type Test.Ord[T] was found for parameter e of method implicitly in object Predef diff --git a/tests/neg/mirror-synthesis-errors-b.check b/tests/neg/mirror-synthesis-errors-b.check index bd846a59f295..d9e394617c9d 100644 --- a/tests/neg/mirror-synthesis-errors-b.check +++ b/tests/neg/mirror-synthesis-errors-b.check @@ -1,38 +1,38 @@ --- Error: tests/neg/mirror-synthesis-errors-b.scala:21:56 -------------------------------------------------------------- +-- [E172] Type Error: tests/neg/mirror-synthesis-errors-b.scala:21:56 -------------------------------------------------- 21 |val testA = summon[Mirror.ProductOf[Cns[Int] & Sm[Int]]] // error: unreleated | ^ |No given instance of type deriving.Mirror.ProductOf[Cns[Int] & Sm[Int]] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.ProductOf[Cns[Int] & Sm[Int]]: type `Cns[Int] & Sm[Int]` is not a generic product because its subpart `Cns[Int] & Sm[Int]` is an intersection of unrelated definitions class Cns and class Sm. --- Error: tests/neg/mirror-synthesis-errors-b.scala:22:56 -------------------------------------------------------------- +-- [E172] Type Error: tests/neg/mirror-synthesis-errors-b.scala:22:56 -------------------------------------------------- 22 |val testB = summon[Mirror.ProductOf[Sm[Int] & Cns[Int]]] // error: unreleated | ^ |No given instance of type deriving.Mirror.ProductOf[Sm[Int] & Cns[Int]] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.ProductOf[Sm[Int] & Cns[Int]]: type `Sm[Int] & Cns[Int]` is not a generic product because its subpart `Sm[Int] & Cns[Int]` is an intersection of unrelated definitions class Sm and class Cns. --- Error: tests/neg/mirror-synthesis-errors-b.scala:23:49 -------------------------------------------------------------- +-- [E172] Type Error: tests/neg/mirror-synthesis-errors-b.scala:23:49 -------------------------------------------------- 23 |val testC = summon[Mirror.Of[Cns[Int] & Sm[Int]]] // error: unreleated | ^ |No given instance of type deriving.Mirror.Of[Cns[Int] & Sm[Int]] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[Cns[Int] & Sm[Int]]: | * type `Cns[Int] & Sm[Int]` is not a generic product because its subpart `Cns[Int] & Sm[Int]` is an intersection of unrelated definitions class Cns and class Sm. | * type `Cns[Int] & Sm[Int]` is not a generic sum because its subpart `Cns[Int] & Sm[Int]` is an intersection of unrelated definitions class Cns and class Sm. --- Error: tests/neg/mirror-synthesis-errors-b.scala:24:49 -------------------------------------------------------------- +-- [E172] Type Error: tests/neg/mirror-synthesis-errors-b.scala:24:49 -------------------------------------------------- 24 |val testD = summon[Mirror.Of[Sm[Int] & Cns[Int]]] // error: unreleated | ^ |No given instance of type deriving.Mirror.Of[Sm[Int] & Cns[Int]] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[Sm[Int] & Cns[Int]]: | * type `Sm[Int] & Cns[Int]` is not a generic product because its subpart `Sm[Int] & Cns[Int]` is an intersection of unrelated definitions class Sm and class Cns. | * type `Sm[Int] & Cns[Int]` is not a generic sum because its subpart `Sm[Int] & Cns[Int]` is an intersection of unrelated definitions class Sm and class Cns. --- Error: tests/neg/mirror-synthesis-errors-b.scala:25:55 -------------------------------------------------------------- +-- [E172] Type Error: tests/neg/mirror-synthesis-errors-b.scala:25:55 -------------------------------------------------- 25 |val testE = summon[Mirror.ProductOf[Sm[Int] & Nn.type]] // error: unreleated | ^ |No given instance of type deriving.Mirror.ProductOf[Sm[Int] & Nn.type] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.ProductOf[Sm[Int] & Nn.type]: type `Sm[Int] & Nn.type` is not a generic product because its subpart `Sm[Int] & Nn.type` is an intersection of unrelated definitions class Sm and object Nn. --- Error: tests/neg/mirror-synthesis-errors-b.scala:26:55 -------------------------------------------------------------- +-- [E172] Type Error: tests/neg/mirror-synthesis-errors-b.scala:26:55 -------------------------------------------------- 26 |val testF = summon[Mirror.ProductOf[Nn.type & Sm[Int]]] // error: unreleated | ^ |No given instance of type deriving.Mirror.ProductOf[Nn.type & Sm[Int]] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.ProductOf[Nn.type & Sm[Int]]: type `Nn.type & Sm[Int]` is not a generic product because its subpart `Nn.type & Sm[Int]` is an intersection of unrelated definitions object Nn and class Sm. --- Error: tests/neg/mirror-synthesis-errors-b.scala:27:54 -------------------------------------------------------------- +-- [E172] Type Error: tests/neg/mirror-synthesis-errors-b.scala:27:54 -------------------------------------------------- 27 |val testG = summon[Mirror.Of[Foo.A.type & Foo.B.type]] // error: unreleated | ^ |No given instance of type deriving.Mirror.Of[(Foo.A : Foo) & (Foo.B : Foo)] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[(Foo.A : Foo) & (Foo.B : Foo)]: | * type `(Foo.A : Foo) & (Foo.B : Foo)` is not a generic product because its subpart `(Foo.A : Foo) & (Foo.B : Foo)` is an intersection of unrelated definitions value A and value B. | * type `(Foo.A : Foo) & (Foo.B : Foo)` is not a generic sum because its subpart `(Foo.A : Foo) & (Foo.B : Foo)` is an intersection of unrelated definitions value A and value B. --- Error: tests/neg/mirror-synthesis-errors-b.scala:28:54 -------------------------------------------------------------- +-- [E172] Type Error: tests/neg/mirror-synthesis-errors-b.scala:28:54 -------------------------------------------------- 28 |val testH = summon[Mirror.Of[Foo.B.type & Foo.A.type]] // error: unreleated | ^ |No given instance of type deriving.Mirror.Of[(Foo.B : Foo) & (Foo.A : Foo)] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[(Foo.B : Foo) & (Foo.A : Foo)]: diff --git a/tests/neg/mirror-synthesis-errors.check b/tests/neg/mirror-synthesis-errors.check index cde026e38910..da795e80bf51 100644 --- a/tests/neg/mirror-synthesis-errors.check +++ b/tests/neg/mirror-synthesis-errors.check @@ -1,40 +1,40 @@ --- Error: tests/neg/mirror-synthesis-errors.scala:21:32 ---------------------------------------------------------------- +-- [E172] Type Error: tests/neg/mirror-synthesis-errors.scala:21:32 ---------------------------------------------------- 21 |val testA = summon[Mirror.Of[A]] // error: Not a sealed trait | ^ |No given instance of type deriving.Mirror.Of[A] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[A]: | * trait A is not a generic product because it is not a case class | * trait A is not a generic sum because it is not a sealed trait --- Error: tests/neg/mirror-synthesis-errors.scala:22:32 ---------------------------------------------------------------- +-- [E172] Type Error: tests/neg/mirror-synthesis-errors.scala:22:32 ---------------------------------------------------- 22 |val testC = summon[Mirror.Of[C]] // error: Does not have subclasses | ^ |No given instance of type deriving.Mirror.Of[C] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[C]: | * trait C is not a generic product because it is not a case class | * trait C is not a generic sum because it does not have subclasses --- Error: tests/neg/mirror-synthesis-errors.scala:23:32 ---------------------------------------------------------------- +-- [E172] Type Error: tests/neg/mirror-synthesis-errors.scala:23:32 ---------------------------------------------------- 23 |val testD = summon[Mirror.Of[D]] // error: child SubD takes more than one parameter list | ^ |No given instance of type deriving.Mirror.Of[D] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[D]: | * class D is not a generic product because it is not a case class | * class D is not a generic sum because its child class SubD is not a generic product because it takes more than one parameter list --- Error: tests/neg/mirror-synthesis-errors.scala:24:38 ---------------------------------------------------------------- +-- [E172] Type Error: tests/neg/mirror-synthesis-errors.scala:24:38 ---------------------------------------------------- 24 |val testSubD = summon[Mirror.Of[SubD]] // error: takes more than one parameter list | ^ |No given instance of type deriving.Mirror.Of[SubD] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[SubD]: | * class SubD is not a generic product because it takes more than one parameter list | * class SubD is not a generic sum because it is not a sealed class --- Error: tests/neg/mirror-synthesis-errors.scala:25:32 ---------------------------------------------------------------- +-- [E172] Type Error: tests/neg/mirror-synthesis-errors.scala:25:32 ---------------------------------------------------- 25 |val testE = summon[Mirror.Of[E]] // error: Not an abstract class | ^ |No given instance of type deriving.Mirror.Of[E] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[E]: | * class E is not a generic product because it is not a case class | * class E is not a generic sum because it is not an abstract class --- Error: tests/neg/mirror-synthesis-errors.scala:26:32 ---------------------------------------------------------------- +-- [E172] Type Error: tests/neg/mirror-synthesis-errors.scala:26:32 ---------------------------------------------------- 26 |val testF = summon[Mirror.Of[F]] // error: No children | ^ |No given instance of type deriving.Mirror.Of[F] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[F]: | * trait F is not a generic product because it is not a case class | * trait F is not a generic sum because it does not have subclasses --- Error: tests/neg/mirror-synthesis-errors.scala:27:36 ---------------------------------------------------------------- +-- [E172] Type Error: tests/neg/mirror-synthesis-errors.scala:27:36 ---------------------------------------------------- 27 |val testG = summon[Mirror.Of[Foo.G]] // error: Has anonymous subclasses | ^ |No given instance of type deriving.Mirror.Of[Foo.G] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[Foo.G]: diff --git a/tests/neg/missing-implicit-2.check b/tests/neg/missing-implicit-2.check index e1994c4bf02d..10f0192d1459 100644 --- a/tests/neg/missing-implicit-2.check +++ b/tests/neg/missing-implicit-2.check @@ -1,4 +1,4 @@ --- Error: tests/neg/missing-implicit-2.scala:4:24 ---------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/missing-implicit-2.scala:4:24 ---------------------------------------------------------- 4 |val f = Future[Unit] { } // error | ^ | Cannot find an implicit ExecutionContext. You might add diff --git a/tests/neg/missing-implicit1.check b/tests/neg/missing-implicit1.check index ccba4b0fa018..c94225aaf0a6 100644 --- a/tests/neg/missing-implicit1.check +++ b/tests/neg/missing-implicit1.check @@ -1,4 +1,4 @@ --- Error: tests/neg/missing-implicit1.scala:17:4 ----------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/missing-implicit1.scala:17:4 ----------------------------------------------------------- 17 | ff // error | ^ |No given instance of type testObjectInstance.Zip[Option] was found for parameter xs of method ff in object testObjectInstance @@ -16,7 +16,7 @@ | | import testObjectInstance.instances.traverseList | --- Error: tests/neg/missing-implicit1.scala:23:42 ---------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/missing-implicit1.scala:23:42 ---------------------------------------------------------- 23 | List(1, 2, 3).traverse(x => Option(x)) // error | ^ |No given instance of type testObjectInstance.Zip[Option] was found for an implicit parameter of method traverse in trait Traverse diff --git a/tests/neg/missing-implicit2.check b/tests/neg/missing-implicit2.check index 705e052c0a43..103c098f5798 100644 --- a/tests/neg/missing-implicit2.check +++ b/tests/neg/missing-implicit2.check @@ -1,4 +1,4 @@ --- Error: tests/neg/missing-implicit2.scala:10:18 ---------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/missing-implicit2.scala:10:18 ---------------------------------------------------------- 10 | f(using xFromY) // error | ^ | No given instance of type Y was found for parameter y of given instance xFromY @@ -7,7 +7,7 @@ | | import test.instances.y | --- Error: tests/neg/missing-implicit2.scala:16:5 ----------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/missing-implicit2.scala:16:5 ----------------------------------------------------------- 16 | f // error | ^ | No given instance of type X was found for parameter x of method f in object test diff --git a/tests/neg/missing-implicit3.check b/tests/neg/missing-implicit3.check index 3cf3b101f3ca..ab87bf99a32a 100644 --- a/tests/neg/missing-implicit3.check +++ b/tests/neg/missing-implicit3.check @@ -1,4 +1,4 @@ --- Error: tests/neg/missing-implicit3.scala:13:36 ---------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/missing-implicit3.scala:13:36 ---------------------------------------------------------- 13 |val sortedFoos = sort(List(new Foo)) // error | ^ | No given instance of type ord.Ord[ord.Foo] was found for an implicit parameter of method sort in package ord. diff --git a/tests/neg/missing-implicit4.check b/tests/neg/missing-implicit4.check index 4cc8a2182b8d..e243c208ecdf 100644 --- a/tests/neg/missing-implicit4.check +++ b/tests/neg/missing-implicit4.check @@ -1,4 +1,4 @@ --- Error: tests/neg/missing-implicit4.scala:14:4 ----------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/missing-implicit4.scala:14:4 ----------------------------------------------------------- 14 | ff // error | ^ | No given instance of type Zip[Option] was found for parameter xs of method ff @@ -16,7 +16,7 @@ | | import instances.traverseList | --- Error: tests/neg/missing-implicit4.scala:20:42 ---------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/missing-implicit4.scala:20:42 ---------------------------------------------------------- 20 | List(1, 2, 3).traverse(x => Option(x)) // error | ^ | No given instance of type Zip[Option] was found for an implicit parameter of method traverse in trait Traverse diff --git a/tests/neg/safeThrowsStrawman.check b/tests/neg/safeThrowsStrawman.check index 6bf1ecdae513..0885404bbb76 100644 --- a/tests/neg/safeThrowsStrawman.check +++ b/tests/neg/safeThrowsStrawman.check @@ -1,4 +1,4 @@ --- Error: tests/neg/safeThrowsStrawman.scala:17:32 --------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/safeThrowsStrawman.scala:17:32 --------------------------------------------------------- 17 | if x then 1 else raise(Fail()) // error | ^ | The capability to throw exception scalax.Fail is missing. @@ -6,7 +6,7 @@ | - A using clause `(using CanThrow[scalax.Fail])` | - A raises clause in a result type such as `X raises scalax.Fail` | - an enclosing `try` that catches scalax.Fail --- Error: tests/neg/safeThrowsStrawman.scala:27:15 --------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/safeThrowsStrawman.scala:27:15 --------------------------------------------------------- 27 | println(bar) // error | ^ | The capability to throw exception Exception is missing. diff --git a/tests/neg/saferExceptions.check b/tests/neg/saferExceptions.check index 5f51ce08d6db..77859e940b2d 100644 --- a/tests/neg/saferExceptions.check +++ b/tests/neg/saferExceptions.check @@ -1,4 +1,4 @@ --- Error: tests/neg/saferExceptions.scala:12:16 ------------------------------------------------------------------------ +-- [E172] Type Error: tests/neg/saferExceptions.scala:12:16 ------------------------------------------------------------ 12 | case 4 => throw Exception() // error | ^^^^^^^^^^^^^^^^^ | The capability to throw exception Exception is missing. @@ -11,7 +11,7 @@ | | import unsafeExceptions.canThrowAny | --- Error: tests/neg/saferExceptions.scala:17:46 ------------------------------------------------------------------------ +-- [E172] Type Error: tests/neg/saferExceptions.scala:17:46 ------------------------------------------------------------ 17 | def baz(x: Int): Int throws Failure = bar(x) // error | ^ | The capability to throw exception java.io.IOException is missing. diff --git a/tests/neg/subtyping.check b/tests/neg/subtyping.check index 832ff6296c52..c0ae1c71e007 100644 --- a/tests/neg/subtyping.check +++ b/tests/neg/subtyping.check @@ -1,8 +1,8 @@ --- Error: tests/neg/subtyping.scala:8:27 ------------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/subtyping.scala:8:27 ------------------------------------------------------------------- 8 | implicitly[B#X <:< A#X] // error: no implicit argument | ^ | Cannot prove that B#X <:< A#X. --- Error: tests/neg/subtyping.scala:12:27 ------------------------------------------------------------------------------ +-- [E172] Type Error: tests/neg/subtyping.scala:12:27 ------------------------------------------------------------------ 12 | implicitly[a.T <:< a.U] // error: no implicit argument | ^ | Cannot prove that a.T <:< a.U. diff --git a/tests/neg/summon-function.check b/tests/neg/summon-function.check index 863d1429d33f..b6ff4feea047 100644 --- a/tests/neg/summon-function.check +++ b/tests/neg/summon-function.check @@ -1,4 +1,4 @@ --- Error: tests/neg/summon-function.scala:2:23 ------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/summon-function.scala:2:23 ------------------------------------------------------------- 2 | summon[Int => String] // error | ^ | No given instance of type Int => String was found for parameter x of method summon in object Predef diff --git a/tests/neg/summonInline.check b/tests/neg/summonInline.check index 6c3839266ce4..e317ed53f8e2 100644 --- a/tests/neg/summonInline.check +++ b/tests/neg/summonInline.check @@ -1,4 +1,4 @@ --- Error: tests/neg/summonInline.scala:19:32 --------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/summonInline.scala:19:32 --------------------------------------------------------------- 19 |val missing1 = summonInlineCheck(1) // error | ^^^^^^^^^^^^^^^^^^^^ | Missing One @@ -9,7 +9,7 @@ 15 | case 1 => summonInline[Missing1] | ^^^^^^^^^^^^^^^^^^^^^^ -------------------------------------------------------------------------------------------------------------------- --- Error: tests/neg/summonInline.scala:20:32 --------------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/summonInline.scala:20:32 --------------------------------------------------------------- 20 |val missing2 = summonInlineCheck(2) // error | ^^^^^^^^^^^^^^^^^^^^ | Missing Two From 02eb18f7378badc6fcf78dfa324b937606a8bece Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 22 Nov 2022 11:20:09 +0100 Subject: [PATCH 05/14] Make override errors real messages --- .../tools/dotc/reporting/ErrorMessageID.scala | 2 +- .../dotty/tools/dotc/reporting/messages.scala | 5308 +++++++++-------- .../tools/dotc/typer/ErrorReporting.scala | 17 + .../dotty/tools/dotc/typer/RefChecks.scala | 35 +- compiler/test-resources/repl/i1370 | 2 +- tests/neg-custom-args/captures/lazylist.check | 2 +- .../neg-custom-args/captures/lazylists2.check | 2 +- tests/neg/abstract-givens.check | 2 +- 8 files changed, 2689 insertions(+), 2681 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index f98c728e04b6..a7bc7f027517 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -176,7 +176,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case JavaEnumParentArgsID // errorNumber: 160 case AlreadyDefinedID // errorNumber: 161 case CaseClassInInlinedCodeID // errorNumber: 162 - case OverrideTypeMismatchErrorID // errorNumber: 163 + case OverrideTypeMismatchErrorID extends ErrorMessageID(isActive = false) // errorNumber: 163 case OverrideErrorID // errorNumber: 164 case MatchableWarningID // errorNumber: 165 case CannotExtendFunctionID // errorNumber: 166 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 3a43e9b5c0b2..63b9fc160443 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -43,2705 +43,2717 @@ import cc.CaptureSet.IdentityCaptRefMap * ``` */ - abstract class SyntaxMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): - def kind = MessageKind.Syntax - - abstract class TypeMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): - def kind = MessageKind.Type - - trait ShowMatchTrace(tps: Type*)(using Context) extends Message: - override def msgSuffix: String = matchReductionAddendum(tps*) - - abstract class TypeMismatchMsg(found: Type, expected: Type)(errorId: ErrorMessageID)(using Context) - extends Message(errorId), ShowMatchTrace(found, expected): - def kind = MessageKind.TypeMismatch - def explain = err.whyNoMatchStr(found, expected) - override def canExplain = true - - abstract class NamingMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): - def kind = MessageKind.Naming - - abstract class DeclarationMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): - def kind = MessageKind.Declaration - - /** A simple not found message (either for idents, or member selection. - * Messages of this class are sometimes dropped in favor of other, more - * specific messages. - */ - abstract class NotFoundMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): - def kind = MessageKind.NotFound - def name: Name - - abstract class PatternMatchMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): - def kind = MessageKind.PatternMatch - - abstract class CyclicMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): - def kind = MessageKind.Cyclic - - abstract class ReferenceMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): - def kind = MessageKind.Reference - - abstract class EmptyCatchOrFinallyBlock(tryBody: untpd.Tree, errNo: ErrorMessageID)(using Context) - extends SyntaxMsg(errNo) { - def explain = { - val tryString = tryBody match { - case Block(Nil, untpd.EmptyTree) => "{}" - case _ => tryBody.show - } - - val code1 = - s"""|import scala.util.control.NonFatal - | - |try $tryString catch { - | case NonFatal(e) => ??? - |}""".stripMargin - - val code2 = - s"""|try $tryString finally { - | // perform your cleanup here! - |}""".stripMargin - - e"""|A ${hl("try")} expression should be followed by some mechanism to handle any exceptions - |thrown. Typically a ${hl("catch")} expression follows the ${hl("try")} and pattern matches - |on any expected exceptions. For example: - | - |$code1 - | - |It is also possible to follow a ${hl("try")} immediately by a ${hl("finally")} - letting the - |exception propagate - but still allowing for some clean up in ${hl("finally")}: - | - |$code2 - | - |It is recommended to use the ${hl("NonFatal")} extractor to catch all exceptions as it - |correctly handles transfer functions like ${hl("return")}.""" - } - } - - class EmptyCatchBlock(tryBody: untpd.Tree)(using Context) - extends EmptyCatchOrFinallyBlock(tryBody, EmptyCatchBlockID) { - def msg = - e"""|The ${hl("catch")} block does not contain a valid expression, try - |adding a case like - ${hl("case e: Exception =>")} to the block""" - } - - class EmptyCatchAndFinallyBlock(tryBody: untpd.Tree)(using Context) - extends EmptyCatchOrFinallyBlock(tryBody, EmptyCatchAndFinallyBlockID) { - def msg = - e"""|A ${hl("try")} without ${hl("catch")} or ${hl("finally")} is equivalent to putting - |its body in a block; no exceptions are handled.""" - } - - class DeprecatedWithOperator()(using Context) - extends SyntaxMsg(DeprecatedWithOperatorID) { - def msg = - e"""${hl("with")} as a type operator has been deprecated; use ${hl("&")} instead""" - def explain = - e"""|Dotty introduces intersection types - ${hl("&")} types. These replace the - |use of the ${hl("with")} keyword. There are a few differences in - |semantics between intersection types and using ${hl("with")}.""" - } - - class CaseClassMissingParamList(cdef: untpd.TypeDef)(using Context) - extends SyntaxMsg(CaseClassMissingParamListID) { - def msg = - e"""|A ${hl("case class")} must have at least one parameter list""" - - def explain = - e"""|${cdef.name} must have at least one parameter list, if you would rather - |have a singleton representation of ${cdef.name}, use a "${hl("case object")}". - |Or, add an explicit ${hl("()")} as a parameter list to ${cdef.name}.""" - } - - class AnonymousFunctionMissingParamType(param: untpd.ValDef, - tree: untpd.Function, - pt: Type) - (using Context) - extends TypeMsg(AnonymousFunctionMissingParamTypeID) { - def msg = { - val ofFun = - if param.name.is(WildcardParamName) - || (MethodType.syntheticParamNames(tree.args.length + 1) contains param.name) - then i" of expanded function:\n$tree" - else "" - - val inferred = - if (pt == WildcardType) "" - else i"\nWhat I could infer was: $pt" - - i"""Missing parameter type - | - |I could not infer the type of the parameter ${param.name}$ofFun.$inferred""" - } - - def explain = "" - } - - class WildcardOnTypeArgumentNotAllowedOnNew()(using Context) - extends SyntaxMsg(WildcardOnTypeArgumentNotAllowedOnNewID) { - def msg = "Type argument must be fully defined" - def explain = - val code1: String = - """ - |object TyperDemo { - | class Team[A] - | val team = new Team[?] - |} - """.stripMargin - - val code2: String = - """ - |object TyperDemo { - | class Team[A] - | val team = new Team[Int] - |} - """.stripMargin - e"""|Wildcard on arguments is not allowed when declaring a new type. - | - |Given the following example: - | - |$code1 - | - |You must complete all the type parameters, for instance: - | - |$code2 """ - } - - - // Type Errors ------------------------------------------------------------ // - class DuplicateBind(bind: untpd.Bind, tree: untpd.CaseDef)(using Context) - extends NamingMsg(DuplicateBindID) { - def msg = e"duplicate pattern variable: ${bind.name}" - - def explain = { - val pat = tree.pat.show - val guard = tree.guard match - case untpd.EmptyTree => "" - case guard => s"if ${guard.show}" - - val body = tree.body match { - case Block(Nil, untpd.EmptyTree) => "" - case body => s" ${body.show}" - } - - val caseDef = s"case $pat$guard => $body" - - e"""|For each ${hl("case")} bound variable names have to be unique. In: - | - |$caseDef - | - |${bind.name} is not unique. Rename one of the bound variables!""" - } - } - - class MissingIdent(tree: untpd.Ident, treeKind: String, val name: Name)(using Context) - extends NotFoundMsg(MissingIdentID) { - def msg = e"Not found: $treeKind$name" - def explain = { - e"""|The identifier for `$treeKind$name` is not bound, that is, - |no declaration for this identifier can be found. - |That can happen, for example, if `$name` or its declaration has either been - |misspelt or if an import is missing.""" - } - } - - class TypeMismatch(found: Type, expected: Type, inTree: Option[untpd.Tree], addenda: => String*)(using Context) - extends TypeMismatchMsg(found, expected)(TypeMismatchID): - - def msg = - // replace constrained TypeParamRefs and their typevars by their bounds where possible - // and the bounds are not f-bounds. - // The idea is that if the bounds are also not-subtypes of each other to report - // the type mismatch on the bounds instead of the original TypeParamRefs, since - // these are usually easier to analyze. We exclude F-bounds since these would - // lead to a recursive infinite expansion. - object reported extends TypeMap, IdentityCaptRefMap: - def setVariance(v: Int) = variance = v - val constraint = mapCtx.typerState.constraint - var fbounded = false - def apply(tp: Type): Type = tp match - case tp: TypeParamRef => - constraint.entry(tp) match - case bounds: TypeBounds => - if variance < 0 then apply(TypeComparer.fullUpperBound(tp)) - else if variance > 0 then apply(TypeComparer.fullLowerBound(tp)) - else tp - case NoType => tp - case instType => apply(instType) - case tp: TypeVar => - apply(tp.stripTypeVar) - case tp: LazyRef => - fbounded = true - tp - case _ => - mapOver(tp) - - val found1 = reported(found) - reported.setVariance(-1) - val expected1 = reported(expected) - val (found2, expected2) = - if (found1 frozen_<:< expected1) || reported.fbounded then (found, expected) - else (found1, expected1) - val postScript = addenda.find(!_.isEmpty) match - case Some(p) => p - case None => - if expected.isTopType || found.isBottomType - then "" - else ctx.typer.importSuggestionAddendum(ViewProto(found.widen, expected)) - val (where, printCtx) = Formatting.disambiguateTypes(found2, expected2) - val whereSuffix = if (where.isEmpty) where else s"\n\n$where" - val (foundStr, expectedStr) = Formatting.typeDiff(found2, expected2)(using printCtx) - s"""|Found: $foundStr - |Required: $expectedStr""".stripMargin - + whereSuffix + postScript - end msg - - override def explain = - val treeStr = inTree.map(x => s"\nTree: ${x.show}").getOrElse("") - treeStr + "\n" + super.explain - - end TypeMismatch - - class NotAMember(site: Type, val name: Name, selected: String, addendum: => String = "")(using Context) - extends NotFoundMsg(NotAMemberID), ShowMatchTrace(site) { - //println(i"site = $site, decls = ${site.decls}, source = ${site.typeSymbol.sourceFile}") //DEBUG - - def msg = { - import core.Flags._ - val maxDist = 3 // maximal number of differences to be considered for a hint - val missing = name.show - - // The symbols of all non-synthetic, non-private members of `site` - // that are of the same type/term kind as the missing member. - def candidates: Set[Symbol] = - for - bc <- site.widen.baseClasses.toSet - sym <- bc.info.decls.filter(sym => - sym.isType == name.isTypeName - && !sym.isConstructor - && !sym.flagsUNSAFE.isOneOf(Synthetic | Private)) - yield sym - - // Calculate Levenshtein distance - def distance(s1: String, s2: String): Int = - val dist = Array.ofDim[Int](s2.length + 1, s1.length + 1) - for - j <- 0 to s2.length - i <- 0 to s1.length - do - dist(j)(i) = - if j == 0 then i - else if i == 0 then j - else if s2(j - 1) == s1(i - 1) then dist(j - 1)(i - 1) - else (dist(j - 1)(i) min dist(j)(i - 1) min dist(j - 1)(i - 1)) + 1 - dist(s2.length)(s1.length) - - // A list of possible candidate symbols with their Levenstein distances - // to the name of the missing member - def closest: List[(Int, Symbol)] = candidates - .toList - .map(sym => (distance(sym.name.show, missing), sym)) - .filter((d, sym) => d <= maxDist && d < missing.length && d < sym.name.show.length) - .sortBy((d, sym) => (d, sym.name.show)) // sort by distance first, alphabetically second - - val enumClause = - if ((name eq nme.values) || (name eq nme.valueOf)) && site.classSymbol.companionClass.isEnumClass then - val kind = if name eq nme.values then i"${nme.values} array" else i"${nme.valueOf} lookup method" - // an assumption is made here that the values and valueOf methods were not generated - // because the enum defines non-singleton cases - i""" - |Although ${site.classSymbol.companionClass} is an enum, it has non-singleton cases, - |meaning a $kind is not defined""" - else - "" - - def prefixEnumClause(addendum: String) = - if enumClause.nonEmpty then s".$enumClause$addendum" else addendum - - val finalAddendum = - if addendum.nonEmpty then prefixEnumClause(addendum) - else closest match - case (d, sym) :: _ => - val siteName = site match - case site: NamedType => site.name.show - case site => i"$site" - val showName = - // Add .type to the name if it is a module - if sym.is(ModuleClass) then s"${sym.name.show}.type" - else sym.name.show - s" - did you mean $siteName.$showName?$enumClause" - case Nil => prefixEnumClause("") - - ex"$selected $name is not a member of ${site.widen}$finalAddendum" - } - - def explain = "" - } - - class EarlyDefinitionsNotSupported()(using Context) - extends SyntaxMsg(EarlyDefinitionsNotSupportedID) { - def msg = "Early definitions are not supported; use trait parameters instead" - - def explain = { - val code1 = - """|trait Logging { - | val f: File - | f.open() - | onExit(f.close()) - | def log(msg: String) = f.write(msg) - |} - | - |class B extends Logging { - | val f = new File("log.data") // triggers a NullPointerException - |} - | - |// early definition gets around the NullPointerException - |class C extends { - | val f = new File("log.data") - |} with Logging""".stripMargin - - val code2 = - """|trait Logging(f: File) { - | f.open() - | onExit(f.close()) - | def log(msg: String) = f.write(msg) - |} - | - |class C extends Logging(new File("log.data"))""".stripMargin - - e"""|Earlier versions of Scala did not support trait parameters and "early - |definitions" (also known as "early initializers") were used as an alternative. - | - |Example of old syntax: - | - |$code1 - | - |The above code can now be written as: - | - |$code2 - |""" - } - } - - class TopLevelImplicitClass(cdef: untpd.TypeDef)(using Context) - extends SyntaxMsg(TopLevelImplicitClassID) { - def msg = e"""An ${hl("implicit class")} may not be top-level""" - - def explain = { - val TypeDef(name, impl @ Template(constr0, parents, self, _)) = cdef: @unchecked - val exampleArgs = - if(constr0.termParamss.isEmpty) "..." - else constr0.termParamss(0).map(_.withMods(untpd.Modifiers()).show).mkString(", ") - def defHasBody[T] = impl.body.exists(!_.isEmpty) - val exampleBody = if (defHasBody) "{\n ...\n }" else "" - e"""|There may not be any method, member or object in scope with the same name as - |the implicit class and a case class automatically gets a companion object with - |the same name created by the compiler which would cause a naming conflict if it - |were allowed. - | | - |To resolve the conflict declare ${cdef.name} inside of an ${hl("object")} then import the class - |from the object at the use site if needed, for example: - | - |object Implicits { - | implicit class ${cdef.name}($exampleArgs)$exampleBody - |} - | - |// At the use site: - |import Implicits.${cdef.name}""" - } - } - - class ImplicitCaseClass(cdef: untpd.TypeDef)(using Context) - extends SyntaxMsg(ImplicitCaseClassID) { - def msg = e"""A ${hl("case class")} may not be defined as ${hl("implicit")}""" - - def explain = - e"""|Implicit classes may not be case classes. Instead use a plain class: - | - |implicit class ${cdef.name}... - | - |""" - } - - class ImplicitClassPrimaryConstructorArity()(using Context) - extends SyntaxMsg(ImplicitClassPrimaryConstructorArityID){ - def msg = "Implicit classes must accept exactly one primary constructor parameter" - def explain = { - val example = "implicit class RichDate(date: java.util.Date)" - e"""Implicit classes may only take one non-implicit argument in their constructor. For example: - | - | $example - | - |While it’s possible to create an implicit class with more than one non-implicit argument, - |such classes aren’t used during implicit lookup. - |""" - } - } - - class ObjectMayNotHaveSelfType(mdef: untpd.ModuleDef)(using Context) - extends SyntaxMsg(ObjectMayNotHaveSelfTypeID) { - def msg = e"""${hl("object")}s must not have a self ${hl("type")}""" - - def explain = { - val untpd.ModuleDef(name, tmpl) = mdef - val ValDef(_, selfTpt, _) = tmpl.self - e"""|${hl("object")}s must not have a self ${hl("type")}: - | - |Consider these alternative solutions: - | - Create a trait or a class instead of an object - | - Let the object extend a trait containing the self type: - | - | object $name extends ${selfTpt.show}""" - } - } - - class RepeatedModifier(modifier: String)(implicit ctx:Context) - extends SyntaxMsg(RepeatedModifierID) { - def msg = e"""Repeated modifier $modifier""" - - def explain = { - val code1 = "private private val Origin = Point(0, 0)" - val code2 = "private final val Origin = Point(0, 0)" - e"""This happens when you accidentally specify the same modifier twice. - | - |Example: - | - |$code1 - | - |instead of - | - |$code2 - | - |""" - } - } - - class InterpolatedStringError()(implicit ctx:Context) - extends SyntaxMsg(InterpolatedStringErrorID) { - def msg = "Error in interpolated string: identifier or block expected" - def explain = { - val code1 = "s\"$new Point(0, 0)\"" - val code2 = "s\"${new Point(0, 0)}\"" - e"""|This usually happens when you forget to place your expressions inside curly braces. - | - |$code1 - | - |should be written as - | - |$code2 - |""" - } - } - - class UnboundPlaceholderParameter()(implicit ctx:Context) - extends SyntaxMsg(UnboundPlaceholderParameterID) { - def msg = e"""Unbound placeholder parameter; incorrect use of ${hl("_")}""" - def explain = - e"""|The ${hl("_")} placeholder syntax was used where it could not be bound. - |Consider explicitly writing the variable binding. - | - |This can be done by replacing ${hl("_")} with a variable (eg. ${hl("x")}) - |and adding ${hl("x =>")} where applicable. - | - |Example before: - | - |${hl("{ _ }")} - | - |Example after: - | - |${hl("x => { x }")} - | - |Another common occurrence for this error is defining a val with ${hl("_")}: - | - |${hl("val a = _")} - | - |But this val definition isn't very useful, it can never be assigned - |another value. And thus will always remain uninitialized. - |Consider replacing the ${hl("val")} with ${hl("var")}: - | - |${hl("var a = _")} - | - |Note that this use of ${hl("_")} is not placeholder syntax, - |but an uninitialized var definition. - |Only fields can be left uninitialized in this manner; local variables - |must be initialized. - | - |Another occurrence for this error is self type definition. - |The ${hl("_")} can be replaced with ${hl("this")}. - | - |Example before: - | - |${hl("trait A { _: B => ... ")} - | - |Example after: - | - |${hl("trait A { this: B => ... ")} - |""" - } - - class IllegalStartSimpleExpr(illegalToken: String)(using Context) - extends SyntaxMsg(IllegalStartSimpleExprID) { - def msg = e"expression expected but ${Red(illegalToken)} found" - def explain = { - e"""|An expression cannot start with ${Red(illegalToken)}.""" - } - } - - class MissingReturnType()(implicit ctx:Context) - extends SyntaxMsg(MissingReturnTypeID) { - def msg = "Missing return type" - def explain = - e"""|An abstract declaration must have a return type. For example: - | - |trait Shape: - | ${hl("def area: Double")} // abstract declaration returning a Double""" - } - - class MissingReturnTypeWithReturnStatement(method: Symbol)(using Context) - extends SyntaxMsg(MissingReturnTypeWithReturnStatementID) { - def msg = e"$method has a return statement; it needs a result type" - def explain = - e"""|If a method contains a ${hl("return")} statement, it must have an - |explicit return type. For example: - | - |${hl("def good: Int /* explicit return type */ = return 1")}""" - } - - class YieldOrDoExpectedInForComprehension()(using Context) - extends SyntaxMsg(YieldOrDoExpectedInForComprehensionID) { - def msg = e"${hl("yield")} or ${hl("do")} expected" - - def explain = - e"""|When the enumerators in a for comprehension are not placed in parentheses or - |braces, a ${hl("do")} or ${hl("yield")} statement is required after the enumerators - |section of the comprehension. - | - |You can save some keystrokes by omitting the parentheses and writing - | - |${hl("val numbers = for i <- 1 to 3 yield i")} - | - | instead of - | - |${hl("val numbers = for (i <- 1 to 3) yield i")} - | - |but the ${hl("yield")} keyword is still required. - | - |For comprehensions that simply perform a side effect without yielding anything - |can also be written without parentheses but a ${hl("do")} keyword has to be - |included. For example, - | - |${hl("for (i <- 1 to 3) println(i)")} - | - |can be written as - | - |${hl("for i <- 1 to 3 do println(i) // notice the 'do' keyword")} - | - |""" - } - - class ProperDefinitionNotFound()(using Context) - extends Message(ProperDefinitionNotFoundID) { - def kind = MessageKind.DocComment - def msg = e"""Proper definition was not found in ${hl("@usecase")}""" - - def explain = { - val noUsecase = - "def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That" - - val usecase = - """|/** Map from List[A] => List[B] - | * - | * @usecase def map[B](f: A => B): List[B] - | */ - |def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That - |""".stripMargin - - e"""|Usecases are only supported for ${hl("def")}s. They exist because with Scala's - |advanced type-system, we sometimes end up with seemingly scary signatures. - |The usage of these methods, however, needs not be - for instance the ${hl("map")} - |function - | - |${hl("List(1, 2, 3).map(2 * _) // res: List(2, 4, 6)")} - | - |is easy to understand and use - but has a rather bulky signature: - | - |$noUsecase - | - |to mitigate this and ease the usage of such functions we have the ${hl("@usecase")} - |annotation for docstrings. Which can be used like this: - | - |$usecase - | - |When creating the docs, the signature of the method is substituted by the - |usecase and the compiler makes sure that it is valid. Because of this, you're - |only allowed to use ${hl("def")}s when defining usecases.""" - } - } - - class ByNameParameterNotSupported(tpe: untpd.Tree)(using Context) - extends SyntaxMsg(ByNameParameterNotSupportedID) { - def msg = e"By-name parameter type ${tpe} not allowed here." - - def explain = - e"""|By-name parameters act like functions that are only evaluated when referenced, - |allowing for lazy evaluation of a parameter. - | - |An example of using a by-name parameter would look like: - |${hl("def func(f: => Boolean) = f // 'f' is evaluated when referenced within the function")} - | - |An example of the syntax of passing an actual function as a parameter: - |${hl("def func(f: (Boolean => Boolean)) = f(true)")} - | - |or: - | - |${hl("def func(f: Boolean => Boolean) = f(true)")} - | - |And the usage could be as such: - |${hl("func(bool => // do something...)")} - |""" - } - - class WrongNumberOfTypeArgs(fntpe: Type, expectedArgs: List[ParamInfo], actual: List[untpd.Tree])(using Context) - extends SyntaxMsg(WrongNumberOfTypeArgsID) { - - private val expectedCount = expectedArgs.length - private val actualCount = actual.length - private val msgPrefix = if (actualCount > expectedCount) "Too many" else "Not enough" - - def msg = - val expectedArgString = expectedArgs - .map(_.paramName.unexpandedName.show) - .mkString("[", ", ", "]") - val actualArgString = actual.map(_.show).mkString("[", ", ", "]") - val prettyName = - try fntpe.termSymbol match - case NoSymbol => fntpe.show - case symbol => symbol.showFullName - catch case NonFatal(ex) => fntpe.show - e"""|$msgPrefix type arguments for $prettyName$expectedArgString - |expected: $expectedArgString - |actual: $actualArgString""" - - def explain = { - val tooManyTypeParams = - """|val tuple2: (Int, String) = (1, "one") - |val list: List[(Int, String)] = List(tuple2)""".stripMargin - - if (actualCount > expectedCount) - e"""|You have supplied too many type parameters - | - |For example List takes a single type parameter (List[A]) - |If you need to hold more types in a list then you need to combine them - |into another data type that can contain the number of types you need, - |In this example one solution would be to use a Tuple: - | - |${tooManyTypeParams}""" - else - e"""|You have not supplied enough type parameters - |If you specify one type parameter then you need to specify every type parameter.""" - } - } - - class IllegalVariableInPatternAlternative(name: Name)(using Context) - extends SyntaxMsg(IllegalVariableInPatternAlternativeID) { - def msg = e"Illegal variable $name in pattern alternative" - def explain = { - val varInAlternative = - """|def g(pair: (Int,Int)): Int = pair match { - | case (1, n) | (n, 1) => n - | case _ => 0 - |}""".stripMargin - - val fixedVarInAlternative = - """|def g(pair: (Int,Int)): Int = pair match { - | case (1, n) => n - | case (n, 1) => n - | case _ => 0 - |}""".stripMargin - - e"""|Variables are not allowed within alternate pattern matches. You can workaround - |this issue by adding additional cases for each alternative. For example, the - |illegal function: - | - |$varInAlternative - |could be implemented by moving each alternative into a separate case: - | - |$fixedVarInAlternative""" - } - } - - class IdentifierExpected(identifier: String)(using Context) - extends SyntaxMsg(IdentifierExpectedID) { - def msg = "identifier expected" - def explain = { - val wrongIdentifier = e"def foo: $identifier = {...}" - val validIdentifier = e"def foo = {...}" - e"""|An identifier expected, but $identifier found. This could be because - |$identifier is not a valid identifier. As a workaround, the compiler could - |infer the type for you. For example, instead of: - | - |$wrongIdentifier - | - |Write your code like: - | - |$validIdentifier - | - |""" - } - } - - class AuxConstructorNeedsNonImplicitParameter()(implicit ctx:Context) - extends SyntaxMsg(AuxConstructorNeedsNonImplicitParameterID) { - def msg = "Auxiliary constructor needs non-implicit parameter list" - def explain = - e"""|Only the primary constructor is allowed an ${hl("implicit")} parameter list; - |auxiliary constructors need non-implicit parameter lists. When a primary - |constructor has an implicit argslist, auxiliary constructors that call the - |primary constructor must specify the implicit value. - | - |To resolve this issue check for: - | - Forgotten parenthesis on ${hl("this")} (${hl("def this() = { ... }")}) - | - Auxiliary constructors specify the implicit value - |""" - } - - class IllegalLiteral()(using Context) - extends SyntaxMsg(IllegalLiteralID) { - def msg = "Illegal literal" - def explain = - e"""|Available literals can be divided into several groups: - | - Integer literals: 0, 21, 0xFFFFFFFF, -42L - | - Floating Point Literals: 0.0, 1e30f, 3.14159f, 1.0e-100, .1 - | - Boolean Literals: true, false - | - Character Literals: 'a', '\u0041', '\n' - | - String Literals: "Hello, World!" - | - null - |""" - } - - class LossyWideningConstantConversion(sourceType: Type, targetType: Type)(using Context) - extends Message(LossyWideningConstantConversionID): - def kind = MessageKind.LossyConversion - def msg = e"""|Widening conversion from $sourceType to $targetType loses precision. - |Write `.to$targetType` instead.""" - def explain = "" - - class PatternMatchExhaustivity(uncoveredFn: => String, hasMore: Boolean)(using Context) - extends Message(PatternMatchExhaustivityID) { - def kind = MessageKind.PatternMatchExhaustivity - lazy val uncovered = uncoveredFn - def msg = - val addendum = if hasMore then "(More unmatched cases are elided)" else "" - e"""|${hl("match")} may not be exhaustive. - | - |It would fail on pattern case: $uncovered - |$addendum""" - - - def explain = - e"""|There are several ways to make the match exhaustive: - | - Add missing cases as shown in the warning - | - If an extractor always return ${hl("Some(...)")}, write ${hl("Some[X]")} for its return type - | - Add a ${hl("case _ => ...")} at the end to match all remaining cases - |""" - } - - class UncheckedTypePattern(msgFn: => String)(using Context) - extends PatternMatchMsg(UncheckedTypePatternID) { - def msg = msgFn - def explain = - e"""|Type arguments and type refinements are erased during compile time, thus it's - |impossible to check them at run-time. - | - |You can either replace the type arguments by ${hl("_")} or use `@unchecked`. - |""" - } - - class MatchCaseUnreachable()(using Context) - extends Message(MatchCaseUnreachableID) { - def kind = MessageKind.MatchCaseUnreachable - def msg = "Unreachable case" - def explain = "" - } - - class MatchCaseOnlyNullWarning()(using Context) - extends PatternMatchMsg(MatchCaseOnlyNullWarningID) { - def msg = e"""Unreachable case except for ${hl("null")} (if this is intentional, consider writing ${hl("case null =>")} instead).""" - def explain = "" - } - - class MatchableWarning(tp: Type, pattern: Boolean)(using Context) - extends TypeMsg(MatchableWarningID) { - def msg = - val kind = if pattern then "pattern selector" else "value" - e"""${kind} should be an instance of Matchable,, - |but it has unmatchable type $tp instead""" - - def explain = - if pattern then - e"""A value of type $tp cannot be the selector of a match expression - |since it is not constrained to be `Matchable`. Matching on unconstrained - |values is disallowed since it can uncover implementation details that - |were intended to be hidden and thereby can violate paramtetricity laws - |for reasoning about programs. - | - |The restriction can be overridden by appending `.asMatchable` to - |the selector value. `asMatchable` needs to be imported from - |scala.compiletime. Example: - | - | import compiletime.asMatchable - | def f[X](x: X) = x.asMatchable match { ... }""" - else - e"""The value can be converted to a `Matchable` by appending `.asMatchable`. - |`asMatchable` needs to be imported from scala.compiletime.""" - } - - class SeqWildcardPatternPos()(using Context) - extends SyntaxMsg(SeqWildcardPatternPosID) { - def msg = e"""${hl("*")} can be used only for last argument""" - def explain = { - val code = - """def sumOfTheFirstTwo(list: List[Int]): Int = list match { - | case List(first, second, x*) => first + second - | case _ => 0 - |}""" - e"""|Sequence wildcard pattern is expected at the end of an argument list. - |This pattern matches any remaining elements in a sequence. - |Consider the following example: - | - |$code - | - |Calling: - | - |${hl("sumOfTheFirstTwo(List(1, 2, 10))")} - | - |would give 3 as a result""" - } - } - - class IllegalStartOfSimplePattern()(using Context) - extends SyntaxMsg(IllegalStartOfSimplePatternID) { - def msg = "pattern expected" - def explain = { - val sipCode = - """def f(x: Int, y: Int) = x match { - | case `y` => ... - |} - """ - val constructorPatternsCode = - """case class Person(name: String, age: Int) - | - |def test(p: Person) = p match { - | case Person(name, age) => ... - |} - """ - val tupplePatternsCode = - """def swap(tuple: (String, Int)): (Int, String) = tuple match { - | case (text, number) => (number, text) - |} - """ - val patternSequencesCode = - """def getSecondValue(list: List[Int]): Int = list match { - | case List(_, second, x:_*) => second - | case _ => 0 - |}""" - e"""|Simple patterns can be divided into several groups: - |- Variable Patterns: ${hl("case x => ...")}. - | It matches any value, and binds the variable name to that value. - | A special case is the wild-card pattern _ which is treated as if it was a fresh - | variable on each occurrence. - | - |- Typed Patterns: ${hl("case x: Int => ...")} or ${hl("case _: Int => ...")}. - | This pattern matches any value matched by the specified type; it binds the variable - | name to that value. - | - |- Literal Patterns: ${hl("case 123 => ...")} or ${hl("case 'A' => ...")}. - | This type of pattern matches any value that is equal to the specified literal. - | - |- Stable Identifier Patterns: - | - | $sipCode - | - | the match succeeds only if the x argument and the y argument of f are equal. - | - |- Constructor Patterns: - | - | $constructorPatternsCode - | - | The pattern binds all object's fields to the variable names (name and age, in this - | case). - | - |- Tuple Patterns: - | - | $tupplePatternsCode - | - | Calling: - | - | ${hl("""swap(("Luftballons", 99)""")} - | - | would give ${hl("""(99, "Luftballons")""")} as a result. - | - |- Pattern Sequences: - | - | $patternSequencesCode - | - | Calling: - | - | ${hl("getSecondValue(List(1, 10, 2))")} - | - | would give 10 as a result. - | This pattern is possible because a companion object for the List class has a method - | with the following signature: - | - | ${hl("def unapplySeq[A](x: List[A]): Some[List[A]]")} - |""" - } - } - - class PkgDuplicateSymbol(existing: Symbol)(using Context) - extends NamingMsg(PkgDuplicateSymbolID) { - def msg = e"Trying to define package with same name as $existing" - def explain = "" - } - - class ExistentialTypesNoLongerSupported()(using Context) - extends SyntaxMsg(ExistentialTypesNoLongerSupportedID) { - def msg = - e"""|Existential types are no longer supported - - |use a wildcard or dependent type instead""" - def explain = - e"""|The use of existential types is no longer supported. - | - |You should use a wildcard or dependent type instead. - | - |For example: - | - |Instead of using ${hl("forSome")} to specify a type variable - | - |${hl("List[T forSome { type T }]")} - | - |Try using a wildcard type variable - | - |${hl("List[?]")} - |""" - } - - class UnboundWildcardType()(using Context) - extends SyntaxMsg(UnboundWildcardTypeID) { - def msg = "Unbound wildcard type" - def explain = - e"""|The wildcard type syntax (${hl("_")}) was used where it could not be bound. - |Replace ${hl("_")} with a non-wildcard type. If the type doesn't matter, - |try replacing ${hl("_")} with ${hl("Any")}. - | - |Examples: - | - |- Parameter lists - | - | Instead of: - | ${hl("def foo(x: _) = ...")} - | - | Use ${hl("Any")} if the type doesn't matter: - | ${hl("def foo(x: Any) = ...")} - | - |- Type arguments - | - | Instead of: - | ${hl("val foo = List[?](1, 2)")} - | - | Use: - | ${hl("val foo = List[Int](1, 2)")} - | - |- Type bounds - | - | Instead of: - | ${hl("def foo[T <: _](x: T) = ...")} - | - | Remove the bounds if the type doesn't matter: - | ${hl("def foo[T](x: T) = ...")} - | - |- ${hl("val")} and ${hl("def")} types - | - | Instead of: - | ${hl("val foo: _ = 3")} - | - | Use: - | ${hl("val foo: Int = 3")} - |""" - } - - class OverridesNothing(member: Symbol)(using Context) - extends DeclarationMsg(OverridesNothingID) { - def msg = e"""${member} overrides nothing""" - - def explain = - e"""|There must be a field or method with the name ${member.name} in a super - |class of ${member.owner} to override it. Did you misspell it? - |Are you extending the right classes? - |""" - } - - class OverridesNothingButNameExists(member: Symbol, existing: List[Denotations.SingleDenotation])(using Context) - extends DeclarationMsg(OverridesNothingButNameExistsID) { - def msg = - val what = - if !existing.exists(_.symbol.hasTargetName(member.targetName)) - then "target name" - else "signature" - e"""${member} has a different $what than the overridden declaration""" - def explain = - val existingDecl: String = existing.map(_.showDcl).mkString(" \n") - e"""|There must be a non-final field or method with the name ${member.name} and the - |same parameter list in a super class of ${member.owner} to override it. - | - | ${member.showDcl} - | - |The super classes of ${member.owner} contain the following members - |named ${member.name}: - | ${existingDecl} - |""" - } - - class OverrideError(override val msg: String)(using Context) extends DeclarationMsg(OverrideErrorID): - def explain = "" - - class OverrideTypeMismatchError(override val msg: String, memberTp: Type, otherTp: Type)(using Context) - extends DeclarationMsg(OverrideTypeMismatchErrorID): - def explain = err.whyNoMatchStr(memberTp, otherTp) - override def canExplain = true - - class ForwardReferenceExtendsOverDefinition(value: Symbol, definition: Symbol)(using Context) - extends ReferenceMsg(ForwardReferenceExtendsOverDefinitionID) { - def msg = e"${definition.name} is a forward reference extending over the definition of ${value.name}" - - def explain = - e"""|${definition.name} is used before you define it, and the definition of ${value.name} - |appears between that use and the definition of ${definition.name}. - | - |Forward references are allowed only, if there are no value definitions between - |the reference and the referred method definition. - | - |Define ${definition.name} before it is used, - |or move the definition of ${value.name} so it does not appear between - |the declaration of ${definition.name} and its use, - |or define ${value.name} as lazy. - |""" - } - - class ExpectedTokenButFound(expected: Token, found: Token)(using Context) - extends SyntaxMsg(ExpectedTokenButFoundID) { - - private def foundText = Tokens.showToken(found) - - def msg = - val expectedText = - if (Tokens.isIdentifier(expected)) "an identifier" - else Tokens.showToken(expected) - e"""${expectedText} expected, but ${foundText} found""" - - def explain = - if (Tokens.isIdentifier(expected) && Tokens.isKeyword(found)) - s""" - |If you want to use $foundText as identifier, you may put it in backticks: `${Tokens.tokenString(found)}`.""".stripMargin - else - "" - } - - class MixedLeftAndRightAssociativeOps(op1: Name, op2: Name, op2LeftAssoc: Boolean)(using Context) - extends SyntaxMsg(MixedLeftAndRightAssociativeOpsID) { - def msg = - val op1Asso: String = if (op2LeftAssoc) "which is right-associative" else "which is left-associative" - val op2Asso: String = if (op2LeftAssoc) "which is left-associative" else "which is right-associative" - e"${op1} (${op1Asso}) and ${op2} ($op2Asso) have same precedence and may not be mixed" - def explain = - s"""|The operators ${op1} and ${op2} are used as infix operators in the same expression, - |but they bind to different sides: - |${op1} is applied to the operand to its ${if (op2LeftAssoc) "right" else "left"} - |${op2} is applied to the operand to its ${if (op2LeftAssoc) "left" else "right"} - |As both have the same precedence the compiler can't decide which to apply first. - | - |You may use parenthesis to make the application order explicit, - |or use method application syntax operand1.${op1}(operand2). - | - |Operators ending in a colon ${hl(":")} are right-associative. All other operators are left-associative. - | - |Infix operator precedence is determined by the operator's first character. Characters are listed - |below in increasing order of precedence, with characters on the same line having the same precedence. - | (all letters) - | | - | ^ - | & - | = ! - | < > - | : - | + - - | * / % - | (all other special characters) - |Operators starting with a letter have lowest precedence, followed by operators starting with `|`, etc. - |""".stripMargin - } - - class CantInstantiateAbstractClassOrTrait(cls: Symbol, isTrait: Boolean)(using Context) - extends TypeMsg(CantInstantiateAbstractClassOrTraitID) { - private val traitOrAbstract = if (isTrait) "a trait" else "abstract" - def msg = e"""${cls.name} is ${traitOrAbstract}; it cannot be instantiated""" - def explain = - e"""|Abstract classes and traits need to be extended by a concrete class or object - |to make their functionality accessible. - | - |You may want to create an anonymous class extending ${cls.name} with - | ${s"class ${cls.name} { }"} - | - |or add a companion object with - | ${s"object ${cls.name} extends ${cls.name}"} - | - |You need to implement any abstract members in both cases. - |""" - } - - class UnreducibleApplication(tycon: Type)(using Context) extends TypeMsg(UnreducibleApplicationID): - def msg = e"unreducible application of higher-kinded type $tycon to wildcard arguments" - def explain = - e"""|An abstract type constructor cannot be applied to wildcard arguments. - |Such applications are equivalent to existential types, which are not - |supported in Scala 3.""" - - class OverloadedOrRecursiveMethodNeedsResultType(cycleSym: Symbol)(using Context) - extends CyclicMsg(OverloadedOrRecursiveMethodNeedsResultTypeID) { - def msg = e"""Overloaded or recursive $cycleSym needs return type""" - def explain = - e"""Case 1: $cycleSym is overloaded - |If there are multiple methods named $cycleSym and at least one definition of - |it calls another, you need to specify the calling method's return type. - | - |Case 2: $cycleSym is recursive - |If $cycleSym calls itself on any path (even through mutual recursion), you need to specify the return type - |of $cycleSym or of a definition it's mutually recursive with. - |""" - } - - class RecursiveValueNeedsResultType(cycleSym: Symbol)(using Context) - extends CyclicMsg(RecursiveValueNeedsResultTypeID) { - def msg = e"""Recursive $cycleSym needs type""" - def explain = - e"""The definition of $cycleSym is recursive and you need to specify its type. - |""" - } - - class CyclicReferenceInvolving(denot: SymDenotation)(using Context) - extends CyclicMsg(CyclicReferenceInvolvingID) { - def msg = - val where = if denot.exists then s" involving $denot" else "" - e"Cyclic reference$where" - def explain = - e"""|$denot is declared as part of a cycle which makes it impossible for the - |compiler to decide upon ${denot.name}'s type. - |To avoid this error, try giving ${denot.name} an explicit type. - |""" - } - - class CyclicReferenceInvolvingImplicit(cycleSym: Symbol)(using Context) - extends CyclicMsg(CyclicReferenceInvolvingImplicitID) { - def msg = e"""Cyclic reference involving implicit $cycleSym""" - def explain = - e"""|$cycleSym is declared as part of a cycle which makes it impossible for the - |compiler to decide upon ${cycleSym.name}'s type. - |This might happen when the right hand-side of $cycleSym's definition involves an implicit search. - |To avoid this error, try giving ${cycleSym.name} an explicit type. - |""" - } - - class SkolemInInferred(tree: tpd.Tree, pt: Type, argument: tpd.Tree)(using Context) - extends TypeMsg(SkolemInInferredID): - def msg = - def argStr = - if argument.isEmpty then "" - else i" from argument of type ${argument.tpe.widen}" - e"""Failure to generate given instance for type $pt$argStr) - | - |I found: $tree - |But the part corresponding to `` is not a reference that can be generated. - |This might be because resolution yielded as given instance a function that is not - |known to be total and side-effect free.""" - def explain = - e"""The part of given resolution that corresponds to `` produced a term that - |is not a stable reference. Therefore a given instance could not be generated. - | - |To trouble-shoot the problem, try to supply an explicit expression instead of - |relying on implicit search at this point.""" - - class SuperQualMustBeParent(qual: untpd.Ident, cls: ClassSymbol)(using Context) - extends ReferenceMsg(SuperQualMustBeParentID) { - def msg = e"""|$qual does not name a parent of $cls""" - def explain = - val parents: Seq[String] = (cls.info.parents map (_.typeSymbol.name.show)).sorted - e"""|When a qualifier ${hl("T")} is used in a ${hl("super")} prefix of the form ${hl("C.super[T]")}, - |${hl("T")} must be a parent type of ${hl("C")}. - | - |In this case, the parents of $cls are: - |${parents.mkString(" - ", "\n - ", "")} - |""" - } - - class VarArgsParamMustComeLast()(using Context) - extends SyntaxMsg(VarArgsParamMustComeLastID) { - def msg = e"""${hl("varargs")} parameter must come last""" - def explain = - e"""|The ${hl("varargs")} field must be the last field in the method signature. - |Attempting to define a field in a method signature after a ${hl("varargs")} field is an error. - |""" - } - - import typer.Typer.BindingPrec - - class AmbiguousReference(name: Name, newPrec: BindingPrec, prevPrec: BindingPrec, prevCtx: Context)(using Context) - extends ReferenceMsg(AmbiguousReferenceID) { - - /** A string which explains how something was bound; Depending on `prec` this is either - * imported by - * or defined in - */ - private def bindingString(prec: BindingPrec, whereFound: Context, qualifier: String = "")(using Context) = { - val howVisible = prec match { - case BindingPrec.Definition => "defined" - case BindingPrec.Inheritance => "inherited" - case BindingPrec.NamedImport => "imported by name" - case BindingPrec.WildImport => "imported" - case BindingPrec.PackageClause => "found" - case BindingPrec.NothingBound => assert(false) - } - if (prec.isImportPrec) { - ex"""$howVisible$qualifier by ${e"${whereFound.importInfo}"}""" - } else - ex"""$howVisible$qualifier in ${e"${whereFound.owner}"}""" - } - - def msg = - i"""|Reference to ${e"$name"} is ambiguous, - |it is both ${bindingString(newPrec, ctx)} - |and ${bindingString(prevPrec, prevCtx, " subsequently")}""" - - def explain = - e"""|The compiler can't decide which of the possible choices you - |are referencing with $name: A definition of lower precedence - |in an inner scope, or a definition with higher precedence in - |an outer scope. - |Note: - | - Definitions in an enclosing scope take precedence over inherited definitions - | - Definitions take precedence over imports - | - Named imports take precedence over wildcard imports - | - You may replace a name when imported using - | ${hl("import")} scala.{ $name => ${name.show + "Tick"} } - |""" - } - - class MethodDoesNotTakeParameters(tree: tpd.Tree)(using Context) - extends TypeMsg(MethodDoesNotTakeParametersId) { - def methodSymbol(using Context): Symbol = - def recur(t: tpd.Tree): Symbol = - val sym = tpd.methPart(t).symbol - if sym == defn.Any_typeCast then - t match - case TypeApply(Select(qual, _), _) => recur(qual) - case _ => sym - else sym - recur(tree) - - def msg = { - val more = if (tree.isInstanceOf[tpd.Apply]) " more" else "" - val meth = methodSymbol - val methStr = if (meth.exists) meth.showLocated else "expression" - e"$methStr does not take$more parameters" - } - - def explain = { - val isNullary = methodSymbol.info.isInstanceOf[ExprType] - val addendum = - if (isNullary) "\nNullary methods may not be called with parenthesis" - else "" - - "You have specified more parameter lists than defined in the method definition(s)." + addendum - } - - } - - class AmbiguousOverload(tree: tpd.Tree, val alternatives: List[SingleDenotation], pt: Type, addendum: String = "")( - implicit ctx: Context) - extends ReferenceMsg(AmbiguousOverloadID) { - private def all = if (alternatives.length == 2) "both" else "all" - def msg = - e"""|Ambiguous overload. The ${err.overloadedAltsStr(alternatives)} - |$all match ${err.expectedTypeStr(pt)}$addendum""" - def explain = - e"""|There are ${alternatives.length} methods that could be referenced as the compiler knows too little - |about the expected type. - |You may specify the expected type e.g. by - |- assigning it to a value with a specified type, or - |- adding a type ascription as in ${hl("instance.myMethod: String => Int")} - |""" - } - - class ReassignmentToVal(name: Name)(using Context) - extends TypeMsg(ReassignmentToValID) { - def msg = e"""Reassignment to val $name""" - def explain = - e"""|You can not assign a new value to $name as values can't be changed. - |Keep in mind that every statement has a value, so you may e.g. use - | ${hl("val")} $name ${hl("= if (condition) 2 else 5")} - |In case you need a reassignable name, you can declare it as - |variable - | ${hl("var")} $name ${hl("=")} ... - |""" - } - - class TypeDoesNotTakeParameters(tpe: Type, params: List[untpd.Tree])(using Context) - extends TypeMsg(TypeDoesNotTakeParametersID) { - private def fboundsAddendum(using Context) = - if tpe.typeSymbol.isAllOf(Provisional | TypeParam) then - "\n(Note that F-bounds of type parameters may not be type lambdas)" - else "" - def msg = e"$tpe does not take type parameters$fboundsAddendum" - def explain = - val ps = - if (params.size == 1) s"a type parameter ${params.head}" - else s"type parameters ${params.map(_.show).mkString(", ")}" - i"""You specified ${NoColor(ps)} for ${e"$tpe"}, which is not - |declared to take any. - |""" - } - - class VarValParametersMayNotBeCallByName(name: TermName, mutable: Boolean)(using Context) - extends SyntaxMsg(VarValParametersMayNotBeCallByNameID) { - def varOrVal = if (mutable) e"${hl("var")}" else e"${hl("val")}" - def msg = s"$varOrVal parameters may not be call-by-name" - def explain = - e"""${hl("var")} and ${hl("val")} parameters of classes and traits may no be call-by-name. In case you - |want the parameter to be evaluated on demand, consider making it just a parameter - |and a ${hl("def")} in the class such as - | ${s"class MyClass(${name}Tick: => String) {"} - | ${s" def $name() = ${name}Tick"} - | ${hl("}")} - |""" - } - - class MissingTypeParameterFor(tpe: Type)(using Context) - extends SyntaxMsg(MissingTypeParameterForID) { - def msg = - if (tpe.derivesFrom(defn.AnyKindClass)) e"${tpe} cannot be used as a value type" - else e"Missing type parameter for ${tpe}" - def explain = "" - } - - class MissingTypeParameterInTypeApp(tpe: Type)(using Context) - extends TypeMsg(MissingTypeParameterInTypeAppID) { - def numParams = tpe.typeParams.length - def parameters = if (numParams == 1) "parameter" else "parameters" - def msg = e"Missing type $parameters for $tpe" - def explain = e"A fully applied type is expected but $tpe takes $numParams $parameters" - } - - class MissingArgument(pname: Name, methString: String)(using Context) - extends TypeMsg(MissingArgumentID): - def msg = - if pname.firstPart contains '$' then s"not enough arguments for $methString" - else s"missing argument for parameter $pname of $methString" - def explain = "" - - class DoesNotConformToBound(tpe: Type, which: String, bound: Type)(using Context) - extends TypeMismatchMsg( - if which == "lower" then bound else tpe, - if which == "lower" then tpe else bound)(DoesNotConformToBoundID): - private def isBounds = tpe match - case TypeBounds(lo, hi) => lo ne hi - case _ => false - override def canExplain = !isBounds - def msg = - if isBounds then - e"Type argument ${tpe} does not overlap with $which bound $bound" - else - e"Type argument ${tpe} does not conform to $which bound $bound" - - class DoesNotConformToSelfType(category: String, selfType: Type, cls: Symbol, - otherSelf: Type, relation: String, other: Symbol)( - implicit ctx: Context) - extends TypeMismatchMsg(selfType, otherSelf)(DoesNotConformToSelfTypeID) { - def msg = e"""$category: self type $selfType of $cls does not conform to self type $otherSelf - |of $relation $other""" - } - - class DoesNotConformToSelfTypeCantBeInstantiated(tp: Type, selfType: Type)( - implicit ctx: Context) - extends TypeMismatchMsg(tp, selfType)(DoesNotConformToSelfTypeCantBeInstantiatedID) { - def msg = e"""$tp does not conform to its self type $selfType; cannot be instantiated""" - } - - class IllegalParameterInit(found: Type, expected: Type, param: Symbol, cls: Symbol)(using Context) - extends TypeMismatchMsg(found, expected)(IllegalParameterInitID): - def msg = - e"""illegal parameter initialization of $param. - | - | The argument passed for $param has type: $found - | but $cls expects $param to have type: $expected""" - - class AbstractMemberMayNotHaveModifier(sym: Symbol, flag: FlagSet)( - implicit ctx: Context) - extends SyntaxMsg(AbstractMemberMayNotHaveModifierID) { - def msg = e"""${hl("abstract")} $sym may not have `${flag.flagsString}` modifier""" - def explain = "" - } - - class TypesAndTraitsCantBeImplicit()(using Context) - extends SyntaxMsg(TypesAndTraitsCantBeImplicitID) { - def msg = e"""${hl("implicit")} modifier cannot be used for types or traits""" - def explain = "" - } - - class OnlyClassesCanBeAbstract(sym: Symbol)( - implicit ctx: Context) - extends SyntaxMsg(OnlyClassesCanBeAbstractID) { - def explain = "" - def msg = e"""${hl("abstract")} modifier can be used only for classes; it should be omitted for abstract members""" - } - - class AbstractOverrideOnlyInTraits(sym: Symbol)( - implicit ctx: Context) - extends SyntaxMsg(AbstractOverrideOnlyInTraitsID) { - def msg = e"""${hl("abstract override")} modifier only allowed for members of traits""" - def explain = "" - } - - class TraitsMayNotBeFinal(sym: Symbol)( - implicit ctx: Context) - extends SyntaxMsg(TraitsMayNotBeFinalID) { - def msg = e"""$sym may not be ${hl("final")}""" - def explain = - "A trait can never be final since it is abstract and must be extended to be useful." - } - - class NativeMembersMayNotHaveImplementation(sym: Symbol)( - implicit ctx: Context) - extends SyntaxMsg(NativeMembersMayNotHaveImplementationID) { - def msg = e"""${hl("@native")} members may not have an implementation""" - def explain = "" - } - - class TraitMayNotDefineNativeMethod(sym: Symbol)( - implicit ctx: Context) - extends SyntaxMsg(TraitMayNotDefineNativeMethodID) { - def msg = e"""A trait cannot define a ${hl("@native")} method.""" - def explain = "" - } - - class OnlyClassesCanHaveDeclaredButUndefinedMembers(sym: Symbol)( - implicit ctx: Context) - extends SyntaxMsg(OnlyClassesCanHaveDeclaredButUndefinedMembersID) { - - def msg = e"""Declaration of $sym not allowed here: only classes can have declared but undefined members""" - def explain = - if sym.is(Mutable) then "Note that variables need to be initialized to be defined." - else "" - } - - class CannotExtendAnyVal(sym: Symbol)(using Context) - extends SyntaxMsg(CannotExtendAnyValID) { - def msg = e"""$sym cannot extend ${hl("AnyVal")}""" - def explain = - e"""Only classes (not traits) are allowed to extend ${hl("AnyVal")}, but traits may extend - |${hl("Any")} to become ${Green("\"universal traits\"")} which may only have ${hl("def")} members. - |Universal traits can be mixed into classes that extend ${hl("AnyVal")}. - |""" - } - - class CannotExtendJavaEnum(sym: Symbol)(using Context) - extends SyntaxMsg(CannotExtendJavaEnumID) { - def msg = e"""$sym cannot extend ${hl("java.lang.Enum")}: only enums defined with the ${hl("enum")} syntax can""" - def explain = "" - } - - class CannotExtendContextFunction(sym: Symbol)(using Context) - extends SyntaxMsg(CannotExtendFunctionID) { - def msg = e"""$sym cannot extend a context function class""" - def explain = "" - } - - class JavaEnumParentArgs(parent: Type)(using Context) - extends TypeMsg(JavaEnumParentArgsID) { - def msg = e"""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._ - def reasonMessage(using Context): String = reason match { - case CannotBeOverridden => "class definitions cannot be overridden" - case DefinedInSelf(self) => - s"""cannot define ${sym.showKind} member with the same name as a ${cls.showKind} member in self reference ${self.name}. - |(Note: this can be resolved by using another name) - |""".stripMargin - } - - def msg = e"""$sym cannot have the same name as ${cls.showLocated} -- """ + reasonMessage - def explain = "" - } - object CannotHaveSameNameAs { - sealed trait Reason - case object CannotBeOverridden extends Reason - case class DefinedInSelf(self: tpd.ValDef) extends Reason - } - - class ValueClassesMayNotDefineInner(valueClass: Symbol, inner: Symbol)(using Context) - extends SyntaxMsg(ValueClassesMayNotDefineInnerID) { - def msg = e"""Value classes may not define an inner class""" - def explain = "" - } - - class ValueClassesMayNotDefineNonParameterField(valueClass: Symbol, field: Symbol)(using Context) - extends SyntaxMsg(ValueClassesMayNotDefineNonParameterFieldID) { - def msg = e"""Value classes may not define non-parameter field""" - def explain = "" - } - - class ValueClassesMayNotDefineASecondaryConstructor(valueClass: Symbol, constructor: Symbol)(using Context) - extends SyntaxMsg(ValueClassesMayNotDefineASecondaryConstructorID) { - def msg = e"""Value classes may not define a secondary constructor""" - def explain = "" - } - - class ValueClassesMayNotContainInitalization(valueClass: Symbol)(using Context) - extends SyntaxMsg(ValueClassesMayNotContainInitalizationID) { - def msg = e"""Value classes may not contain initialization statements""" - def explain = "" - } - - class ValueClassesMayNotBeAbstract(valueClass: Symbol)(using Context) - extends SyntaxMsg(ValueClassesMayNotBeAbstractID) { - def msg = e"""Value classes may not be ${hl("abstract")}""" - def explain = "" - } - - class ValueClassesMayNotBeContainted(valueClass: Symbol)(using Context) - extends SyntaxMsg(ValueClassesMayNotBeContaintedID) { - private def localOrMember = if (valueClass.owner.isTerm) "local class" else "member of another class" - def msg = s"""Value classes may not be a $localOrMember""" - def explain = "" - } - - class ValueClassesMayNotWrapAnotherValueClass(valueClass: Symbol)(using Context) - extends SyntaxMsg(ValueClassesMayNotWrapAnotherValueClassID) { - def msg = """A value class may not wrap another user-defined value class""" - def explain = "" - } - - class ValueClassParameterMayNotBeAVar(valueClass: Symbol, param: Symbol)(using Context) - extends SyntaxMsg(ValueClassParameterMayNotBeAVarID) { - def msg = e"""A value class parameter may not be a ${hl("var")}""" - def explain = - e"""A value class must have exactly one ${hl("val")} parameter.""" - } - - class ValueClassNeedsOneValParam(valueClass: Symbol)(using Context) - extends SyntaxMsg(ValueClassNeedsExactlyOneValParamID) { - def msg = e"""Value class needs one ${hl("val")} parameter""" - def explain = "" - } - - class ValueClassParameterMayNotBeCallByName(valueClass: Symbol, param: Symbol)(using Context) - extends SyntaxMsg(ValueClassParameterMayNotBeCallByNameID) { - def msg = s"Value class parameter `${param.name}` may not be call-by-name" - def explain = "" - } - - class SuperCallsNotAllowedInlineable(symbol: Symbol)(using Context) - extends SyntaxMsg(SuperCallsNotAllowedInlineableID) { - def msg = e"Super call not allowed in inlineable $symbol" - def explain = "Method inlining prohibits calling superclass methods, as it may lead to confusion about which super is being called." - } - - class NotAPath(tp: Type, usage: String)(using Context) extends TypeMsg(NotAPathID): - def msg = e"$tp is not a valid $usage, since it is not an immutable path" - def explain = - i"""An immutable path is - | - a reference to an immutable value, or - | - a reference to `this`, or - | - a selection of an immutable path with an immutable value.""" - - class WrongNumberOfParameters(expected: Int)(using Context) - extends SyntaxMsg(WrongNumberOfParametersID) { - def msg = s"Wrong number of parameters, expected: $expected" - def explain = "" - } - - class DuplicatePrivateProtectedQualifier()(using Context) - extends SyntaxMsg(DuplicatePrivateProtectedQualifierID) { - def msg = "Duplicate private/protected qualifier" - def explain = - e"It is not allowed to combine `private` and `protected` modifiers even if they are qualified to different scopes" - } - - class ExpectedStartOfTopLevelDefinition()(using Context) - extends SyntaxMsg(ExpectedStartOfTopLevelDefinitionID) { - def msg = "Expected start of definition" - def explain = - e"You have to provide either ${hl("class")}, ${hl("trait")}, ${hl("object")}, or ${hl("enum")} definitions after qualifiers" - } - - class NoReturnFromInlineable(owner: Symbol)(using Context) - extends SyntaxMsg(NoReturnFromInlineableID) { - def msg = e"No explicit ${hl("return")} allowed from inlineable $owner" - def explain = - e"""Methods marked with ${hl("inline")} modifier may not use ${hl("return")} statements. - |Instead, you should rely on the last expression's value being - |returned from a method. - |""" - } - - class ReturnOutsideMethodDefinition(owner: Symbol)(using Context) - extends SyntaxMsg(ReturnOutsideMethodDefinitionID) { - def msg = e"${hl("return")} outside method definition" - def explain = - e"""You used ${hl("return")} in ${owner}. - |${hl("return")} is a keyword and may only be used within method declarations. - |""" - } - - class ExtendFinalClass(clazz:Symbol, finalClazz: Symbol)(using Context) - extends SyntaxMsg(ExtendFinalClassID) { - def msg = e"$clazz cannot extend ${hl("final")} $finalClazz" - def explain = - e"""A class marked with the ${hl("final")} keyword cannot be extended""" - } - - class ExpectedTypeBoundOrEquals(found: Token)(using Context) - extends SyntaxMsg(ExpectedTypeBoundOrEqualsID) { - def msg = e"${hl("=")}, ${hl(">:")}, or ${hl("<:")} expected, but ${Tokens.showToken(found)} found" +abstract class SyntaxMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): + def kind = MessageKind.Syntax + +abstract class TypeMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): + def kind = MessageKind.Type + +trait ShowMatchTrace(tps: Type*)(using Context) extends Message: + override def msgSuffix: String = matchReductionAddendum(tps*) + +abstract class TypeMismatchMsg(found: Type, expected: Type)(errorId: ErrorMessageID)(using Context) +extends Message(errorId), ShowMatchTrace(found, expected): + def kind = MessageKind.TypeMismatch + def explain = err.whyNoMatchStr(found, expected) + override def canExplain = true + +abstract class NamingMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): + def kind = MessageKind.Naming + +abstract class DeclarationMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): + def kind = MessageKind.Declaration + +/** A simple not found message (either for idents, or member selection. + * Messages of this class are sometimes dropped in favor of other, more + * specific messages. + */ +abstract class NotFoundMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): + def kind = MessageKind.NotFound + def name: Name + +abstract class PatternMatchMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): + def kind = MessageKind.PatternMatch + +abstract class CyclicMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): + def kind = MessageKind.Cyclic + +abstract class ReferenceMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): + def kind = MessageKind.Reference + +abstract class EmptyCatchOrFinallyBlock(tryBody: untpd.Tree, errNo: ErrorMessageID)(using Context) +extends SyntaxMsg(errNo) { + def explain = { + val tryString = tryBody match { + case Block(Nil, untpd.EmptyTree) => "{}" + case _ => tryBody.show + } - def explain = - e"""Type parameters and abstract types may be constrained by a type bound. - |Such type bounds limit the concrete values of the type variables and possibly - |reveal more information about the members of such types. - | - |A lower type bound ${hl("B >: A")} expresses that the type variable ${hl("B")} - |refers to a supertype of type ${hl("A")}. + val code1 = + s"""|import scala.util.control.NonFatal | - |An upper type bound ${hl("T <: A")} declares that type variable ${hl("T")} - |refers to a subtype of type ${hl("A")}. - |""" - } + |try $tryString catch { + | case NonFatal(e) => ??? + |}""".stripMargin - class ClassAndCompanionNameClash(cls: Symbol, other: Symbol)(using Context) - extends NamingMsg(ClassAndCompanionNameClashID) { - def msg = - val name = cls.name.stripModuleClassSuffix - e"Name clash: both ${cls.owner} and its companion object defines $name" - def explain = - e"""|A ${cls.kindString} and its companion object cannot both define a ${hl("class")}, ${hl("trait")} or ${hl("object")} with the same name: - | - ${cls.owner} defines ${cls} - | - ${other.owner} defines ${other}""" - } + val code2 = + s"""|try $tryString finally { + | // perform your cleanup here! + |}""".stripMargin - class TailrecNotApplicable(symbol: Symbol)(using Context) - extends SyntaxMsg(TailrecNotApplicableID) { - def msg = { - val reason = - if (!symbol.is(Method)) e"$symbol isn't a method" - else if (symbol.is(Deferred)) e"$symbol is abstract" - else if (!symbol.isEffectivelyFinal) e"$symbol is neither ${hl("private")} nor ${hl("final")} so can be overridden" - else e"$symbol contains no recursive calls" + e"""|A ${hl("try")} expression should be followed by some mechanism to handle any exceptions + |thrown. Typically a ${hl("catch")} expression follows the ${hl("try")} and pattern matches + |on any expected exceptions. For example: + | + |$code1 + | + |It is also possible to follow a ${hl("try")} immediately by a ${hl("finally")} - letting the + |exception propagate - but still allowing for some clean up in ${hl("finally")}: + | + |$code2 + | + |It is recommended to use the ${hl("NonFatal")} extractor to catch all exceptions as it + |correctly handles transfer functions like ${hl("return")}.""" + } +} + +class EmptyCatchBlock(tryBody: untpd.Tree)(using Context) +extends EmptyCatchOrFinallyBlock(tryBody, EmptyCatchBlockID) { + def msg = + e"""|The ${hl("catch")} block does not contain a valid expression, try + |adding a case like - ${hl("case e: Exception =>")} to the block""" +} + +class EmptyCatchAndFinallyBlock(tryBody: untpd.Tree)(using Context) +extends EmptyCatchOrFinallyBlock(tryBody, EmptyCatchAndFinallyBlockID) { + def msg = + e"""|A ${hl("try")} without ${hl("catch")} or ${hl("finally")} is equivalent to putting + |its body in a block; no exceptions are handled.""" +} + +class DeprecatedWithOperator()(using Context) +extends SyntaxMsg(DeprecatedWithOperatorID) { + def msg = + e"""${hl("with")} as a type operator has been deprecated; use ${hl("&")} instead""" + def explain = + e"""|Dotty introduces intersection types - ${hl("&")} types. These replace the + |use of the ${hl("with")} keyword. There are a few differences in + |semantics between intersection types and using ${hl("with")}.""" +} + +class CaseClassMissingParamList(cdef: untpd.TypeDef)(using Context) +extends SyntaxMsg(CaseClassMissingParamListID) { + def msg = + e"""|A ${hl("case class")} must have at least one parameter list""" + + def explain = + e"""|${cdef.name} must have at least one parameter list, if you would rather + |have a singleton representation of ${cdef.name}, use a "${hl("case object")}". + |Or, add an explicit ${hl("()")} as a parameter list to ${cdef.name}.""" +} + +class AnonymousFunctionMissingParamType(param: untpd.ValDef, + tree: untpd.Function, + pt: Type) + (using Context) +extends TypeMsg(AnonymousFunctionMissingParamTypeID) { + def msg = { + val ofFun = + if param.name.is(WildcardParamName) + || (MethodType.syntheticParamNames(tree.args.length + 1) contains param.name) + then i" of expanded function:\n$tree" + else "" - s"TailRec optimisation not applicable, $reason" - } - def explain = "" - } + val inferred = + if (pt == WildcardType) "" + else i"\nWhat I could infer was: $pt" - class FailureToEliminateExistential(tp: Type, tp1: Type, tp2: Type, boundSyms: List[Symbol], classRoot: Symbol)(using Context) - extends Message(FailureToEliminateExistentialID) { - def kind = MessageKind.Compatibility - def msg = - val originalType = ctx.printer.dclsText(boundSyms, "; ").show - e"""An existential type that came from a Scala-2 classfile for $classRoot - |cannot be mapped accurately to a Scala-3 equivalent. - |original type : $tp forSome ${originalType} - |reduces to : $tp1 - |type used instead: $tp2 - |This choice can cause follow-on type errors or hide type errors. - |Proceed at own risk.""" - def explain = - e"""Existential types in their full generality are no longer supported. - |Scala-3 does applications of class types to wildcard type arguments. - |Other forms of existential types that come from Scala-2 classfiles - |are only approximated in a best-effort way.""" + i"""Missing parameter type + | + |I could not infer the type of the parameter ${param.name}$ofFun.$inferred""" } - class OnlyFunctionsCanBeFollowedByUnderscore(tp: Type)(using Context) - extends SyntaxMsg(OnlyFunctionsCanBeFollowedByUnderscoreID) { - def msg = e"Only function types can be followed by ${hl("_")} but the current expression has type $tp" - def explain = - e"""The syntax ${hl("x _")} is no longer supported if ${hl("x")} is not a function. - |To convert to a function value, you need to explicitly write ${hl("() => x")}""" - } + def explain = "" +} - class MissingEmptyArgumentList(method: String)(using Context) - extends SyntaxMsg(MissingEmptyArgumentListID) { - def msg = e"$method must be called with ${hl("()")} argument" - def explain = { - val codeExample = - """def next(): T = ... - |next // is expanded to next()""" +class WildcardOnTypeArgumentNotAllowedOnNew()(using Context) +extends SyntaxMsg(WildcardOnTypeArgumentNotAllowedOnNewID) { + def msg = "Type argument must be fully defined" + def explain = + val code1: String = + """ + |object TyperDemo { + | class Team[A] + | val team = new Team[?] + |} + """.stripMargin - e"""Previously an empty argument list () was implicitly inserted when calling a nullary method without arguments. E.g. - | - |$codeExample - | - |In Dotty, this idiom is an error. The application syntax has to follow exactly the parameter syntax. - |Excluded from this rule are methods that are defined in Java or that override methods defined in Java.""" + val code2: String = + """ + |object TyperDemo { + | class Team[A] + | val team = new Team[Int] + |} + """.stripMargin + e"""|Wildcard on arguments is not allowed when declaring a new type. + | + |Given the following example: + | + |$code1 + | + |You must complete all the type parameters, for instance: + | + |$code2 """ +} + + +// Type Errors ------------------------------------------------------------ // +class DuplicateBind(bind: untpd.Bind, tree: untpd.CaseDef)(using Context) +extends NamingMsg(DuplicateBindID) { + def msg = e"duplicate pattern variable: ${bind.name}" + + def explain = { + val pat = tree.pat.show + val guard = tree.guard match + case untpd.EmptyTree => "" + case guard => s"if ${guard.show}" + + val body = tree.body match { + case Block(Nil, untpd.EmptyTree) => "" + case body => s" ${body.show}" } - } - - class DuplicateNamedTypeParameter(name: Name)(using Context) - extends SyntaxMsg(DuplicateNamedTypeParameterID) { - def msg = e"Type parameter $name was defined multiple times." - def explain = "" - } - - class UndefinedNamedTypeParameter(undefinedName: Name, definedNames: List[Name])(using Context) - extends SyntaxMsg(UndefinedNamedTypeParameterID) { - def msg = e"Type parameter $undefinedName is undefined. Expected one of ${definedNames.map(_.show).mkString(", ")}." - def explain = "" - } - class IllegalStartOfStatement(what: String, isModifier: Boolean, isStat: Boolean)(using Context) extends SyntaxMsg(IllegalStartOfStatementID) { - def msg = - if isStat then - "this kind of statement is not allowed here" + val caseDef = s"case $pat$guard => $body" + + e"""|For each ${hl("case")} bound variable names have to be unique. In: + | + |$caseDef + | + |${bind.name} is not unique. Rename one of the bound variables!""" + } +} + +class MissingIdent(tree: untpd.Ident, treeKind: String, val name: Name)(using Context) +extends NotFoundMsg(MissingIdentID) { + def msg = e"Not found: $treeKind$name" + def explain = { + e"""|The identifier for `$treeKind$name` is not bound, that is, + |no declaration for this identifier can be found. + |That can happen, for example, if `$name` or its declaration has either been + |misspelt or if an import is missing.""" + } +} + +class TypeMismatch(found: Type, expected: Type, inTree: Option[untpd.Tree], addenda: => String*)(using Context) + extends TypeMismatchMsg(found, expected)(TypeMismatchID): + + def msg = + // replace constrained TypeParamRefs and their typevars by their bounds where possible + // and the bounds are not f-bounds. + // The idea is that if the bounds are also not-subtypes of each other to report + // the type mismatch on the bounds instead of the original TypeParamRefs, since + // these are usually easier to analyze. We exclude F-bounds since these would + // lead to a recursive infinite expansion. + object reported extends TypeMap, IdentityCaptRefMap: + def setVariance(v: Int) = variance = v + val constraint = mapCtx.typerState.constraint + var fbounded = false + def apply(tp: Type): Type = tp match + case tp: TypeParamRef => + constraint.entry(tp) match + case bounds: TypeBounds => + if variance < 0 then apply(TypeComparer.fullUpperBound(tp)) + else if variance > 0 then apply(TypeComparer.fullLowerBound(tp)) + else tp + case NoType => tp + case instType => apply(instType) + case tp: TypeVar => + apply(tp.stripTypeVar) + case tp: LazyRef => + fbounded = true + tp + case _ => + mapOver(tp) + + val found1 = reported(found) + reported.setVariance(-1) + val expected1 = reported(expected) + val (found2, expected2) = + if (found1 frozen_<:< expected1) || reported.fbounded then (found, expected) + else (found1, expected1) + val postScript = addenda.find(!_.isEmpty) match + case Some(p) => p + case None => + if expected.isTopType || found.isBottomType + then "" + else ctx.typer.importSuggestionAddendum(ViewProto(found.widen, expected)) + val (where, printCtx) = Formatting.disambiguateTypes(found2, expected2) + val whereSuffix = if (where.isEmpty) where else s"\n\n$where" + val (foundStr, expectedStr) = Formatting.typeDiff(found2, expected2)(using printCtx) + s"""|Found: $foundStr + |Required: $expectedStr""".stripMargin + + whereSuffix + postScript + end msg + + override def explain = + val treeStr = inTree.map(x => s"\nTree: ${x.show}").getOrElse("") + treeStr + "\n" + super.explain + +end TypeMismatch + +class NotAMember(site: Type, val name: Name, selected: String, addendum: => String = "")(using Context) +extends NotFoundMsg(NotAMemberID), ShowMatchTrace(site) { + //println(i"site = $site, decls = ${site.decls}, source = ${site.typeSymbol.sourceFile}") //DEBUG + + def msg = { + import core.Flags._ + val maxDist = 3 // maximal number of differences to be considered for a hint + val missing = name.show + + // The symbols of all non-synthetic, non-private members of `site` + // that are of the same type/term kind as the missing member. + def candidates: Set[Symbol] = + for + bc <- site.widen.baseClasses.toSet + sym <- bc.info.decls.filter(sym => + sym.isType == name.isTypeName + && !sym.isConstructor + && !sym.flagsUNSAFE.isOneOf(Synthetic | Private)) + yield sym + + // Calculate Levenshtein distance + def distance(s1: String, s2: String): Int = + val dist = Array.ofDim[Int](s2.length + 1, s1.length + 1) + for + j <- 0 to s2.length + i <- 0 to s1.length + do + dist(j)(i) = + if j == 0 then i + else if i == 0 then j + else if s2(j - 1) == s1(i - 1) then dist(j - 1)(i - 1) + else (dist(j - 1)(i) min dist(j)(i - 1) min dist(j - 1)(i - 1)) + 1 + dist(s2.length)(s1.length) + + // A list of possible candidate symbols with their Levenstein distances + // to the name of the missing member + def closest: List[(Int, Symbol)] = candidates + .toList + .map(sym => (distance(sym.name.show, missing), sym)) + .filter((d, sym) => d <= maxDist && d < missing.length && d < sym.name.show.length) + .sortBy((d, sym) => (d, sym.name.show)) // sort by distance first, alphabetically second + + val enumClause = + if ((name eq nme.values) || (name eq nme.valueOf)) && site.classSymbol.companionClass.isEnumClass then + val kind = if name eq nme.values then i"${nme.values} array" else i"${nme.valueOf} lookup method" + // an assumption is made here that the values and valueOf methods were not generated + // because the enum defines non-singleton cases + i""" + |Although ${site.classSymbol.companionClass} is an enum, it has non-singleton cases, + |meaning a $kind is not defined""" else - val addendum = if isModifier then ": this modifier is not allowed here" else "" - s"Illegal start of $what$addendum" - def explain = - i"""A statement is an import or export, a definition or an expression. - |Some statements are only allowed in certain contexts""" - } + "" - class TraitIsExpected(symbol: Symbol)(using Context) extends SyntaxMsg(TraitIsExpectedID) { - def msg = e"$symbol is not a trait" - def explain = { - val errorCodeExample = - """class A - |class B + def prefixEnumClause(addendum: String) = + if enumClause.nonEmpty then s".$enumClause$addendum" else addendum + + val finalAddendum = + if addendum.nonEmpty then prefixEnumClause(addendum) + else closest match + case (d, sym) :: _ => + val siteName = site match + case site: NamedType => site.name.show + case site => i"$site" + val showName = + // Add .type to the name if it is a module + if sym.is(ModuleClass) then s"${sym.name.show}.type" + else sym.name.show + s" - did you mean $siteName.$showName?$enumClause" + case Nil => prefixEnumClause("") + + ex"$selected $name is not a member of ${site.widen}$finalAddendum" + } + + def explain = "" +} + +class EarlyDefinitionsNotSupported()(using Context) +extends SyntaxMsg(EarlyDefinitionsNotSupportedID) { + def msg = "Early definitions are not supported; use trait parameters instead" + + def explain = { + val code1 = + """|trait Logging { + | val f: File + | f.open() + | onExit(f.close()) + | def log(msg: String) = f.write(msg) + |} | - |val a = new A with B // will fail with a compile error - class B is not a trait""".stripMargin - val codeExample = - """class A - |trait B + |class B extends Logging { + | val f = new File("log.data") // triggers a NullPointerException + |} | - |val a = new A with B // compiles normally""".stripMargin - - e"""Only traits can be mixed into classes using a ${hl("with")} keyword. - |Consider the following example: - | - |$errorCodeExample - | - |The example mentioned above would fail because B is not a trait. - |But if you make B a trait it will be compiled without any errors: - | - |$codeExample - |""" - } - } - - class TraitRedefinedFinalMethodFromAnyRef(method: Symbol)(using Context) extends SyntaxMsg(TraitRedefinedFinalMethodFromAnyRefID) { - def msg = e"Traits cannot redefine final $method from ${hl("class AnyRef")}." - def explain = "" - } - - class AlreadyDefined(name: Name, owner: Symbol, conflicting: Symbol)(using Context) extends NamingMsg(AlreadyDefinedID): - def msg = - def where: String = - if conflicting.effectiveOwner.is(Package) && conflicting.associatedFile != null then - i" in ${conflicting.associatedFile}" - else if conflicting.owner == owner then "" - else i" in ${conflicting.owner}" - def note = - if owner.is(Method) || conflicting.is(Method) then - "\n\nNote that overloaded methods must all be defined in the same group of toplevel definitions" - else "" - if conflicting.isTerm != name.isTermName then - e"$name clashes with $conflicting$where; the two must be defined together" - else - e"$name is already defined as $conflicting$where$note" - def explain = "" - - class PackageNameAlreadyDefined(pkg: Symbol)(using Context) extends NamingMsg(PackageNameAlreadyDefinedID) { - def msg = - def where = if pkg.associatedFile == null then "" else s" in ${pkg.associatedFile}" - e"""${pkg.name} is the name of $pkg$where. - |It cannot be used at the same time as the name of a package.""" - def explain = - def or = if pkg.associatedFile == null then "" else " or delete the containing class file" - e"""An ${hl("object")} or other toplevel definition cannot have the same name as an existing ${hl("package")}. - |Rename either one of them$or.""" - } + |// early definition gets around the NullPointerException + |class C extends { + | val f = new File("log.data") + |} with Logging""".stripMargin - class UnapplyInvalidNumberOfArguments(qual: untpd.Tree, argTypes: List[Type])(using Context) - extends SyntaxMsg(UnapplyInvalidNumberOfArgumentsID) { - def msg = e"Wrong number of argument patterns for $qual; expected: ($argTypes%, %)" - def explain = - e"""The Unapply method of $qual was used with incorrect number of arguments. - |Expected usage would be something like: - |case $qual(${argTypes.map(_ => '_')}%, %) => ... - | - |where subsequent arguments would have following types: ($argTypes%, %). - |""" - } + val code2 = + """|trait Logging(f: File) { + | f.open() + | onExit(f.close()) + | def log(msg: String) = f.write(msg) + |} + | + |class C extends Logging(new File("log.data"))""".stripMargin + + e"""|Earlier versions of Scala did not support trait parameters and "early + |definitions" (also known as "early initializers") were used as an alternative. + | + |Example of old syntax: + | + |$code1 + | + |The above code can now be written as: + | + |$code2 + |""" + } +} + +class TopLevelImplicitClass(cdef: untpd.TypeDef)(using Context) +extends SyntaxMsg(TopLevelImplicitClassID) { + def msg = e"""An ${hl("implicit class")} may not be top-level""" + + def explain = { + val TypeDef(name, impl @ Template(constr0, parents, self, _)) = cdef: @unchecked + val exampleArgs = + if(constr0.termParamss.isEmpty) "..." + else constr0.termParamss(0).map(_.withMods(untpd.Modifiers()).show).mkString(", ") + def defHasBody[T] = impl.body.exists(!_.isEmpty) + val exampleBody = if (defHasBody) "{\n ...\n }" else "" + e"""|There may not be any method, member or object in scope with the same name as + |the implicit class and a case class automatically gets a companion object with + |the same name created by the compiler which would cause a naming conflict if it + |were allowed. + | | + |To resolve the conflict declare ${cdef.name} inside of an ${hl("object")} then import the class + |from the object at the use site if needed, for example: + | + |object Implicits { + | implicit class ${cdef.name}($exampleArgs)$exampleBody + |} + | + |// At the use site: + |import Implicits.${cdef.name}""" + } +} + +class ImplicitCaseClass(cdef: untpd.TypeDef)(using Context) +extends SyntaxMsg(ImplicitCaseClassID) { + def msg = e"""A ${hl("case class")} may not be defined as ${hl("implicit")}""" + + def explain = + e"""|Implicit classes may not be case classes. Instead use a plain class: + | + |implicit class ${cdef.name}... + | + |""" +} + +class ImplicitClassPrimaryConstructorArity()(using Context) +extends SyntaxMsg(ImplicitClassPrimaryConstructorArityID){ + def msg = "Implicit classes must accept exactly one primary constructor parameter" + def explain = { + val example = "implicit class RichDate(date: java.util.Date)" + e"""Implicit classes may only take one non-implicit argument in their constructor. For example: + | + | $example + | + |While it’s possible to create an implicit class with more than one non-implicit argument, + |such classes aren’t used during implicit lookup. + |""" + } +} + +class ObjectMayNotHaveSelfType(mdef: untpd.ModuleDef)(using Context) +extends SyntaxMsg(ObjectMayNotHaveSelfTypeID) { + def msg = e"""${hl("object")}s must not have a self ${hl("type")}""" + + def explain = { + val untpd.ModuleDef(name, tmpl) = mdef + val ValDef(_, selfTpt, _) = tmpl.self + e"""|${hl("object")}s must not have a self ${hl("type")}: + | + |Consider these alternative solutions: + | - Create a trait or a class instead of an object + | - Let the object extend a trait containing the self type: + | + | object $name extends ${selfTpt.show}""" + } +} + +class RepeatedModifier(modifier: String)(implicit ctx:Context) +extends SyntaxMsg(RepeatedModifierID) { + def msg = e"""Repeated modifier $modifier""" + + def explain = { + val code1 = "private private val Origin = Point(0, 0)" + val code2 = "private final val Origin = Point(0, 0)" + e"""This happens when you accidentally specify the same modifier twice. + | + |Example: + | + |$code1 + | + |instead of + | + |$code2 + | + |""" + } +} + +class InterpolatedStringError()(implicit ctx:Context) +extends SyntaxMsg(InterpolatedStringErrorID) { + def msg = "Error in interpolated string: identifier or block expected" + def explain = { + val code1 = "s\"$new Point(0, 0)\"" + val code2 = "s\"${new Point(0, 0)}\"" + e"""|This usually happens when you forget to place your expressions inside curly braces. + | + |$code1 + | + |should be written as + | + |$code2 + |""" + } +} + +class UnboundPlaceholderParameter()(implicit ctx:Context) +extends SyntaxMsg(UnboundPlaceholderParameterID) { + def msg = e"""Unbound placeholder parameter; incorrect use of ${hl("_")}""" + def explain = + e"""|The ${hl("_")} placeholder syntax was used where it could not be bound. + |Consider explicitly writing the variable binding. + | + |This can be done by replacing ${hl("_")} with a variable (eg. ${hl("x")}) + |and adding ${hl("x =>")} where applicable. + | + |Example before: + | + |${hl("{ _ }")} + | + |Example after: + | + |${hl("x => { x }")} + | + |Another common occurrence for this error is defining a val with ${hl("_")}: + | + |${hl("val a = _")} + | + |But this val definition isn't very useful, it can never be assigned + |another value. And thus will always remain uninitialized. + |Consider replacing the ${hl("val")} with ${hl("var")}: + | + |${hl("var a = _")} + | + |Note that this use of ${hl("_")} is not placeholder syntax, + |but an uninitialized var definition. + |Only fields can be left uninitialized in this manner; local variables + |must be initialized. + | + |Another occurrence for this error is self type definition. + |The ${hl("_")} can be replaced with ${hl("this")}. + | + |Example before: + | + |${hl("trait A { _: B => ... ")} + | + |Example after: + | + |${hl("trait A { this: B => ... ")} + |""" +} + +class IllegalStartSimpleExpr(illegalToken: String)(using Context) +extends SyntaxMsg(IllegalStartSimpleExprID) { + def msg = e"expression expected but ${Red(illegalToken)} found" + def explain = { + e"""|An expression cannot start with ${Red(illegalToken)}.""" + } +} + +class MissingReturnType()(implicit ctx:Context) +extends SyntaxMsg(MissingReturnTypeID) { + def msg = "Missing return type" + def explain = + e"""|An abstract declaration must have a return type. For example: + | + |trait Shape: + | ${hl("def area: Double")} // abstract declaration returning a Double""" +} + +class MissingReturnTypeWithReturnStatement(method: Symbol)(using Context) +extends SyntaxMsg(MissingReturnTypeWithReturnStatementID) { + def msg = e"$method has a return statement; it needs a result type" + def explain = + e"""|If a method contains a ${hl("return")} statement, it must have an + |explicit return type. For example: + | + |${hl("def good: Int /* explicit return type */ = return 1")}""" +} + +class YieldOrDoExpectedInForComprehension()(using Context) +extends SyntaxMsg(YieldOrDoExpectedInForComprehensionID) { + def msg = e"${hl("yield")} or ${hl("do")} expected" + + def explain = + e"""|When the enumerators in a for comprehension are not placed in parentheses or + |braces, a ${hl("do")} or ${hl("yield")} statement is required after the enumerators + |section of the comprehension. + | + |You can save some keystrokes by omitting the parentheses and writing + | + |${hl("val numbers = for i <- 1 to 3 yield i")} + | + | instead of + | + |${hl("val numbers = for (i <- 1 to 3) yield i")} + | + |but the ${hl("yield")} keyword is still required. + | + |For comprehensions that simply perform a side effect without yielding anything + |can also be written without parentheses but a ${hl("do")} keyword has to be + |included. For example, + | + |${hl("for (i <- 1 to 3) println(i)")} + | + |can be written as + | + |${hl("for i <- 1 to 3 do println(i) // notice the 'do' keyword")} + | + |""" +} + +class ProperDefinitionNotFound()(using Context) +extends Message(ProperDefinitionNotFoundID) { + def kind = MessageKind.DocComment + def msg = e"""Proper definition was not found in ${hl("@usecase")}""" + + def explain = { + val noUsecase = + "def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That" + + val usecase = + """|/** Map from List[A] => List[B] + | * + | * @usecase def map[B](f: A => B): List[B] + | */ + |def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That + |""".stripMargin - class UnapplyInvalidReturnType(unapplyResult: Type, unapplyName: Name)(using Context) - extends DeclarationMsg(UnapplyInvalidReturnTypeID) { - def msg = - val addendum = - if Feature.migrateTo3 && unapplyName == nme.unapplySeq - then "\nYou might want to try to rewrite the extractor to use `unapply` instead." - else "" - e"""| ${Red(i"$unapplyResult")} is not a valid result type of an $unapplyName method of an ${Magenta("extractor")}.$addendum""" - def explain = if (unapplyName.show == "unapply") - e""" - |To be used as an extractor, an unapply method has to return a type that either: - | - has members ${Magenta("isEmpty: Boolean")} and ${Magenta("get: S")} (usually an ${Green("Option[S]")}) - | - is a ${Green("Boolean")} - | - is a ${Green("Product")} (like a ${Magenta("Tuple2[T1, T2]")}) - | - |class A(val i: Int) - | - |object B { - | def unapply(a: A): ${Green("Option[Int]")} = Some(a.i) - |} - | - |object C { - | def unapply(a: A): ${Green("Boolean")} = a.i == 2 - |} - | - |object D { - | def unapply(a: A): ${Green("(Int, Int)")} = (a.i, a.i) - |} - | - |object Test { - | def test(a: A) = a match { - | ${Magenta("case B(1)")} => 1 - | ${Magenta("case a @ C()")} => 2 - | ${Magenta("case D(3, 3)")} => 3 - | } - |} - """ + e"""|Usecases are only supported for ${hl("def")}s. They exist because with Scala's + |advanced type-system, we sometimes end up with seemingly scary signatures. + |The usage of these methods, however, needs not be - for instance the ${hl("map")} + |function + | + |${hl("List(1, 2, 3).map(2 * _) // res: List(2, 4, 6)")} + | + |is easy to understand and use - but has a rather bulky signature: + | + |$noUsecase + | + |to mitigate this and ease the usage of such functions we have the ${hl("@usecase")} + |annotation for docstrings. Which can be used like this: + | + |$usecase + | + |When creating the docs, the signature of the method is substituted by the + |usecase and the compiler makes sure that it is valid. Because of this, you're + |only allowed to use ${hl("def")}s when defining usecases.""" + } +} + +class ByNameParameterNotSupported(tpe: untpd.Tree)(using Context) +extends SyntaxMsg(ByNameParameterNotSupportedID) { + def msg = e"By-name parameter type ${tpe} not allowed here." + + def explain = + e"""|By-name parameters act like functions that are only evaluated when referenced, + |allowing for lazy evaluation of a parameter. + | + |An example of using a by-name parameter would look like: + |${hl("def func(f: => Boolean) = f // 'f' is evaluated when referenced within the function")} + | + |An example of the syntax of passing an actual function as a parameter: + |${hl("def func(f: (Boolean => Boolean)) = f(true)")} + | + |or: + | + |${hl("def func(f: Boolean => Boolean) = f(true)")} + | + |And the usage could be as such: + |${hl("func(bool => // do something...)")} + |""" +} + +class WrongNumberOfTypeArgs(fntpe: Type, expectedArgs: List[ParamInfo], actual: List[untpd.Tree])(using Context) +extends SyntaxMsg(WrongNumberOfTypeArgsID) { + + private val expectedCount = expectedArgs.length + private val actualCount = actual.length + private val msgPrefix = if (actualCount > expectedCount) "Too many" else "Not enough" + + def msg = + val expectedArgString = expectedArgs + .map(_.paramName.unexpandedName.show) + .mkString("[", ", ", "]") + val actualArgString = actual.map(_.show).mkString("[", ", ", "]") + val prettyName = + try fntpe.termSymbol match + case NoSymbol => fntpe.show + case symbol => symbol.showFullName + catch case NonFatal(ex) => fntpe.show + e"""|$msgPrefix type arguments for $prettyName$expectedArgString + |expected: $expectedArgString + |actual: $actualArgString""" + + def explain = { + val tooManyTypeParams = + """|val tuple2: (Int, String) = (1, "one") + |val list: List[(Int, String)] = List(tuple2)""".stripMargin + + if (actualCount > expectedCount) + e"""|You have supplied too many type parameters + | + |For example List takes a single type parameter (List[A]) + |If you need to hold more types in a list then you need to combine them + |into another data type that can contain the number of types you need, + |In this example one solution would be to use a Tuple: + | + |${tooManyTypeParams}""" else - e""" - |To be used as an extractor, an unapplySeq method has to return a type which has members - |${Magenta("isEmpty: Boolean")} and ${Magenta("get: S")} where ${Magenta("S <: Seq[V]")} (usually an ${Green("Option[Seq[V]]")}): - | - |object CharList { - | def unapplySeq(s: String): ${Green("Option[Seq[Char]")} = Some(s.toList) - | - | "example" match { - | ${Magenta("case CharList(c1, c2, c3, c4, _, _, _)")} => - | println(s"$$c1,$$c2,$$c3,$$c4") - | case _ => - | println("Expected *exactly* 7 characters!") - | } - |} - """ - } - - class StaticFieldsOnlyAllowedInObjects(member: Symbol)(using Context) extends SyntaxMsg(StaticFieldsOnlyAllowedInObjectsID) { - def msg = e"${hl("@static")} $member in ${member.owner} must be defined inside a static ${hl("object")}." - def explain = - e"${hl("@static")} members are only allowed inside objects." - } - - class StaticFieldsShouldPrecedeNonStatic(member: Symbol, defns: List[tpd.Tree])(using Context) extends SyntaxMsg(StaticFieldsShouldPrecedeNonStaticID) { - def msg = e"${hl("@static")} $member in ${member.owner} must be defined before non-static fields." - def explain = { - val nonStatics = defns.takeWhile(_.symbol != member).take(3).filter(_.isInstanceOf[tpd.ValDef]) - val codeExample = s"""object ${member.owner.name.firstPart} { - | @static ${member} = ... - | ${nonStatics.map(m => s"${m.symbol} = ...").mkString("\n ")} - | ... - |}""" - e"""The fields annotated with @static should precede any non @static fields. - |This ensures that we do not introduce surprises for users in initialization order of this class. - |Static field are initialized when class loading the code of Foo. - |Non static fields are only initialized the first time that Foo is accessed. - | - |The definition of ${member.name} should have been before the non ${hl("@static val")}s: - |$codeExample - |""" - } - } - - class CyclicInheritance(symbol: Symbol, addendum: => String)(using Context) extends SyntaxMsg(CyclicInheritanceID) { - def msg = e"Cyclic inheritance: $symbol extends itself$addendum" - def explain = { - val codeExample = "class A extends A" - - e"""Cyclic inheritance is prohibited in Dotty. - |Consider the following example: - | - |$codeExample - | - |The example mentioned above would fail because this type of inheritance hierarchy - |creates a "cycle" where a not yet defined class A extends itself which makes - |impossible to instantiate an object of this class""" - } - } - - class BadSymbolicReference(denot: SymDenotation)(using Context) - extends ReferenceMsg(BadSymbolicReferenceID) { - def msg = { - val denotationOwner = denot.owner - val denotationName = ctx.fresh.setSetting(ctx.settings.YdebugNames, true).printer.nameString(denot.name) - val file = denot.symbol.associatedFile - val (location, src) = - if (file != null) (s" in $file", file.toString) - else ("", "the signature") - - e"""Bad symbolic reference. A signature$location - |refers to $denotationName in ${denotationOwner.showKind} ${denotationOwner.showFullName} which is not available. - |It may be completely missing from the current classpath, or the version on - |the classpath might be incompatible with the version used when compiling $src.""" - } - - def explain = "" - } - - class UnableToExtendSealedClass(pclazz: Symbol)(using Context) extends SyntaxMsg(UnableToExtendSealedClassID) { - def msg = e"Cannot extend ${hl("sealed")} $pclazz in a different source file" - def explain = "A sealed class or trait can only be extended in the same file as its declaration" - } - - class SymbolHasUnparsableVersionNumber(symbol: Symbol, errorMessage: String)(using Context) - extends SyntaxMsg(SymbolHasUnparsableVersionNumberID) { - def msg = e"${symbol.showLocated} has an unparsable version number: $errorMessage" - def explain = - e"""The ${symbol.showLocated} is marked with ${hl("@migration")} indicating it has changed semantics - |between versions and the ${hl("-Xmigration")} settings is used to warn about constructs - |whose behavior may have changed since version change.""" - } - - class SymbolChangedSemanticsInVersion( - symbol: Symbol, - migrationVersion: ScalaVersion, - migrationMessage: String - )(using Context) extends SyntaxMsg(SymbolChangedSemanticsInVersionID) { - def msg = e"${symbol.showLocated} has changed semantics in version $migrationVersion: $migrationMessage" - def explain = - e"""The ${symbol.showLocated} is marked with ${hl("@migration")} indicating it has changed semantics - |between versions and the ${hl("-Xmigration")} settings is used to warn about constructs - |whose behavior may have changed since version change.""" - } + e"""|You have not supplied enough type parameters + |If you specify one type parameter then you need to specify every type parameter.""" + } +} + +class IllegalVariableInPatternAlternative(name: Name)(using Context) +extends SyntaxMsg(IllegalVariableInPatternAlternativeID) { + def msg = e"Illegal variable $name in pattern alternative" + def explain = { + val varInAlternative = + """|def g(pair: (Int,Int)): Int = pair match { + | case (1, n) | (n, 1) => n + | case _ => 0 + |}""".stripMargin - class UnableToEmitSwitch()(using Context) - extends SyntaxMsg(UnableToEmitSwitchID) { - def msg = e"Could not emit switch for ${hl("@switch")} annotated match" - def explain = { - val codeExample = - """val ConstantB = 'B' - |final val ConstantC = 'C' - |def tokenMe(ch: Char) = (ch: @switch) match { - | case '\t' | '\n' => 1 - | case 'A' => 2 - | case ConstantB => 3 // a non-literal may prevent switch generation: this would not compile - | case ConstantC => 4 // a constant value is allowed - | case _ => 5 + val fixedVarInAlternative = + """|def g(pair: (Int,Int)): Int = pair match { + | case (1, n) => n + | case (n, 1) => n + | case _ => 0 |}""".stripMargin - e"""If annotated with ${hl("@switch")}, the compiler will verify that the match has been compiled to a - |tableswitch or lookupswitch and issue an error if it instead compiles into a series of conditional - |expressions. Example usage: + e"""|Variables are not allowed within alternate pattern matches. You can workaround + |this issue by adding additional cases for each alternative. For example, the + |illegal function: + | + |$varInAlternative + |could be implemented by moving each alternative into a separate case: + | + |$fixedVarInAlternative""" + } +} + +class IdentifierExpected(identifier: String)(using Context) +extends SyntaxMsg(IdentifierExpectedID) { + def msg = "identifier expected" + def explain = { + val wrongIdentifier = e"def foo: $identifier = {...}" + val validIdentifier = e"def foo = {...}" + e"""|An identifier expected, but $identifier found. This could be because + |$identifier is not a valid identifier. As a workaround, the compiler could + |infer the type for you. For example, instead of: + | + |$wrongIdentifier + | + |Write your code like: + | + |$validIdentifier + | + |""" + } +} + +class AuxConstructorNeedsNonImplicitParameter()(implicit ctx:Context) +extends SyntaxMsg(AuxConstructorNeedsNonImplicitParameterID) { + def msg = "Auxiliary constructor needs non-implicit parameter list" + def explain = + e"""|Only the primary constructor is allowed an ${hl("implicit")} parameter list; + |auxiliary constructors need non-implicit parameter lists. When a primary + |constructor has an implicit argslist, auxiliary constructors that call the + |primary constructor must specify the implicit value. + | + |To resolve this issue check for: + | - Forgotten parenthesis on ${hl("this")} (${hl("def this() = { ... }")}) + | - Auxiliary constructors specify the implicit value + |""" +} + +class IllegalLiteral()(using Context) +extends SyntaxMsg(IllegalLiteralID) { + def msg = "Illegal literal" + def explain = + e"""|Available literals can be divided into several groups: + | - Integer literals: 0, 21, 0xFFFFFFFF, -42L + | - Floating Point Literals: 0.0, 1e30f, 3.14159f, 1.0e-100, .1 + | - Boolean Literals: true, false + | - Character Literals: 'a', '\u0041', '\n' + | - String Literals: "Hello, World!" + | - null + |""" +} + +class LossyWideningConstantConversion(sourceType: Type, targetType: Type)(using Context) +extends Message(LossyWideningConstantConversionID): + def kind = MessageKind.LossyConversion + def msg = e"""|Widening conversion from $sourceType to $targetType loses precision. + |Write `.to$targetType` instead.""" + def explain = "" + +class PatternMatchExhaustivity(uncoveredFn: => String, hasMore: Boolean)(using Context) +extends Message(PatternMatchExhaustivityID) { + def kind = MessageKind.PatternMatchExhaustivity + lazy val uncovered = uncoveredFn + def msg = + val addendum = if hasMore then "(More unmatched cases are elided)" else "" + e"""|${hl("match")} may not be exhaustive. + | + |It would fail on pattern case: $uncovered + |$addendum""" + + + def explain = + e"""|There are several ways to make the match exhaustive: + | - Add missing cases as shown in the warning + | - If an extractor always return ${hl("Some(...)")}, write ${hl("Some[X]")} for its return type + | - Add a ${hl("case _ => ...")} at the end to match all remaining cases + |""" +} + +class UncheckedTypePattern(msgFn: => String)(using Context) + extends PatternMatchMsg(UncheckedTypePatternID) { + def msg = msgFn + def explain = + e"""|Type arguments and type refinements are erased during compile time, thus it's + |impossible to check them at run-time. + | + |You can either replace the type arguments by ${hl("_")} or use `@unchecked`. + |""" +} + +class MatchCaseUnreachable()(using Context) +extends Message(MatchCaseUnreachableID) { + def kind = MessageKind.MatchCaseUnreachable + def msg = "Unreachable case" + def explain = "" +} + +class MatchCaseOnlyNullWarning()(using Context) +extends PatternMatchMsg(MatchCaseOnlyNullWarningID) { + def msg = e"""Unreachable case except for ${hl("null")} (if this is intentional, consider writing ${hl("case null =>")} instead).""" + def explain = "" +} + +class MatchableWarning(tp: Type, pattern: Boolean)(using Context) +extends TypeMsg(MatchableWarningID) { + def msg = + val kind = if pattern then "pattern selector" else "value" + e"""${kind} should be an instance of Matchable,, + |but it has unmatchable type $tp instead""" + + def explain = + if pattern then + e"""A value of type $tp cannot be the selector of a match expression + |since it is not constrained to be `Matchable`. Matching on unconstrained + |values is disallowed since it can uncover implementation details that + |were intended to be hidden and thereby can violate paramtetricity laws + |for reasoning about programs. | - |$codeExample + |The restriction can be overridden by appending `.asMatchable` to + |the selector value. `asMatchable` needs to be imported from + |scala.compiletime. Example: | - |The compiler will not apply the optimisation if: - |- the matched value is not of type ${hl("Int")}, ${hl("Byte")}, ${hl("Short")} or ${hl("Char")} - |- the matched value is not a constant literal - |- there are less than three cases""" - } - } - - class MissingCompanionForStatic(member: Symbol)(using Context) - extends SyntaxMsg(MissingCompanionForStaticID) { - def msg = e"${member.owner} does not have a companion class" - def explain = - e"An object that contains ${hl("@static")} members must have a companion class." - } - - class PolymorphicMethodMissingTypeInParent(rsym: Symbol, parentSym: Symbol)(using Context) - extends SyntaxMsg(PolymorphicMethodMissingTypeInParentID) { - def msg = e"Polymorphic refinement $rsym without matching type in parent $parentSym is no longer allowed" - def explain = - e"""Polymorphic $rsym is not allowed in the structural refinement of $parentSym because - |$rsym does not override any method in $parentSym. Structural refinement does not allow for - |polymorphic methods.""" - } - - class ParamsNoInline(owner: Symbol)(using Context) - extends SyntaxMsg(ParamsNoInlineID) { - def msg = e"""${hl("inline")} modifier can only be used for parameters of inline methods""" - def explain = "" - } - - class JavaSymbolIsNotAValue(symbol: Symbol)(using Context) extends TypeMsg(JavaSymbolIsNotAValueID) { - def msg = { - val kind = - if (symbol is Package) e"$symbol" - else e"Java defined ${hl("class " + symbol.name)}" - - s"$kind is not a value" - } - def explain = "" - } - - class DoubleDefinition(decl: Symbol, previousDecl: Symbol, base: Symbol)(using Context) extends NamingMsg(DoubleDefinitionID) { - def msg = { - def nameAnd = if (decl.name != previousDecl.name) " name and" else "" - def erasedType = if ctx.erasedTypes then i" ${decl.info}" else "" - def details(using Context): String = - if (decl.isRealMethod && previousDecl.isRealMethod) { - import Signature.MatchDegree._ - - // compare the signatures when both symbols represent methods - decl.signature.matchDegree(previousDecl.signature) match { - case NoMatch => - // If the signatures don't match at all at the current phase, then - // they might match after erasure. - if ctx.phase.id <= elimErasedValueTypePhase.id then - atPhase(elimErasedValueTypePhase.next)(details) - else - "" // shouldn't be reachable - case ParamMatch => - "have matching parameter types." - case MethodNotAMethodMatch => - "neither has parameters." - case FullMatch => - val hint = - if !decl.hasAnnotation(defn.TargetNameAnnot) - && !previousDecl.hasAnnotation(defn.TargetNameAnnot) - then - i""" - | - |Consider adding a @targetName annotation to one of the conflicting definitions - |for disambiguation.""" - else "" - i"have the same$nameAnd type$erasedType after erasure.$hint" - } - } + | import compiletime.asMatchable + | def f[X](x: X) = x.asMatchable match { ... }""" + else + e"""The value can be converted to a `Matchable` by appending `.asMatchable`. + |`asMatchable` needs to be imported from scala.compiletime.""" +} + +class SeqWildcardPatternPos()(using Context) +extends SyntaxMsg(SeqWildcardPatternPosID) { + def msg = e"""${hl("*")} can be used only for last argument""" + def explain = { + val code = + """def sumOfTheFirstTwo(list: List[Int]): Int = list match { + | case List(first, second, x*) => first + second + | case _ => 0 + |}""" + e"""|Sequence wildcard pattern is expected at the end of an argument list. + |This pattern matches any remaining elements in a sequence. + |Consider the following example: + | + |$code + | + |Calling: + | + |${hl("sumOfTheFirstTwo(List(1, 2, 10))")} + | + |would give 3 as a result""" + } +} + +class IllegalStartOfSimplePattern()(using Context) +extends SyntaxMsg(IllegalStartOfSimplePatternID) { + def msg = "pattern expected" + def explain = { + val sipCode = + """def f(x: Int, y: Int) = x match { + | case `y` => ... + |} + """ + val constructorPatternsCode = + """case class Person(name: String, age: Int) + | + |def test(p: Person) = p match { + | case Person(name, age) => ... + |} + """ + val tupplePatternsCode = + """def swap(tuple: (String, Int)): (Int, String) = tuple match { + | case (text, number) => (number, text) + |} + """ + val patternSequencesCode = + """def getSecondValue(list: List[Int]): Int = list match { + | case List(_, second, x:_*) => second + | case _ => 0 + |}""" + e"""|Simple patterns can be divided into several groups: + |- Variable Patterns: ${hl("case x => ...")}. + | It matches any value, and binds the variable name to that value. + | A special case is the wild-card pattern _ which is treated as if it was a fresh + | variable on each occurrence. + | + |- Typed Patterns: ${hl("case x: Int => ...")} or ${hl("case _: Int => ...")}. + | This pattern matches any value matched by the specified type; it binds the variable + | name to that value. + | + |- Literal Patterns: ${hl("case 123 => ...")} or ${hl("case 'A' => ...")}. + | This type of pattern matches any value that is equal to the specified literal. + | + |- Stable Identifier Patterns: + | + | $sipCode + | + | the match succeeds only if the x argument and the y argument of f are equal. + | + |- Constructor Patterns: + | + | $constructorPatternsCode + | + | The pattern binds all object's fields to the variable names (name and age, in this + | case). + | + |- Tuple Patterns: + | + | $tupplePatternsCode + | + | Calling: + | + | ${hl("""swap(("Luftballons", 99)""")} + | + | would give ${hl("""(99, "Luftballons")""")} as a result. + | + |- Pattern Sequences: + | + | $patternSequencesCode + | + | Calling: + | + | ${hl("getSecondValue(List(1, 10, 2))")} + | + | would give 10 as a result. + | This pattern is possible because a companion object for the List class has a method + | with the following signature: + | + | ${hl("def unapplySeq[A](x: List[A]): Some[List[A]]")} + |""" + } +} + +class PkgDuplicateSymbol(existing: Symbol)(using Context) +extends NamingMsg(PkgDuplicateSymbolID) { + def msg = e"Trying to define package with same name as $existing" + def explain = "" +} + +class ExistentialTypesNoLongerSupported()(using Context) +extends SyntaxMsg(ExistentialTypesNoLongerSupportedID) { + def msg = + e"""|Existential types are no longer supported - + |use a wildcard or dependent type instead""" + def explain = + e"""|The use of existential types is no longer supported. + | + |You should use a wildcard or dependent type instead. + | + |For example: + | + |Instead of using ${hl("forSome")} to specify a type variable + | + |${hl("List[T forSome { type T }]")} + | + |Try using a wildcard type variable + | + |${hl("List[?]")} + |""" +} + +class UnboundWildcardType()(using Context) +extends SyntaxMsg(UnboundWildcardTypeID) { + def msg = "Unbound wildcard type" + def explain = + e"""|The wildcard type syntax (${hl("_")}) was used where it could not be bound. + |Replace ${hl("_")} with a non-wildcard type. If the type doesn't matter, + |try replacing ${hl("_")} with ${hl("Any")}. + | + |Examples: + | + |- Parameter lists + | + | Instead of: + | ${hl("def foo(x: _) = ...")} + | + | Use ${hl("Any")} if the type doesn't matter: + | ${hl("def foo(x: Any) = ...")} + | + |- Type arguments + | + | Instead of: + | ${hl("val foo = List[?](1, 2)")} + | + | Use: + | ${hl("val foo = List[Int](1, 2)")} + | + |- Type bounds + | + | Instead of: + | ${hl("def foo[T <: _](x: T) = ...")} + | + | Remove the bounds if the type doesn't matter: + | ${hl("def foo[T](x: T) = ...")} + | + |- ${hl("val")} and ${hl("def")} types + | + | Instead of: + | ${hl("val foo: _ = 3")} + | + | Use: + | ${hl("val foo: Int = 3")} + |""" +} + +class OverridesNothing(member: Symbol)(using Context) +extends DeclarationMsg(OverridesNothingID) { + def msg = e"""${member} overrides nothing""" + + def explain = + e"""|There must be a field or method with the name ${member.name} in a super + |class of ${member.owner} to override it. Did you misspell it? + |Are you extending the right classes? + |""" +} + +class OverridesNothingButNameExists(member: Symbol, existing: List[Denotations.SingleDenotation])(using Context) +extends DeclarationMsg(OverridesNothingButNameExistsID) { + def msg = + val what = + if !existing.exists(_.symbol.hasTargetName(member.targetName)) + then "target name" + else "signature" + e"""${member} has a different $what than the overridden declaration""" + def explain = + val existingDecl: String = existing.map(_.showDcl).mkString(" \n") + e"""|There must be a non-final field or method with the name ${member.name} and the + |same parameter list in a super class of ${member.owner} to override it. + | + | ${member.showDcl} + | + |The super classes of ${member.owner} contain the following members + |named ${member.name}: + | ${existingDecl} + |""" +} + +class OverrideError( + core: Context ?=> String, base: Type, + member: Symbol, other: Symbol, + memberTp: Type, otherTp: Type)(using Context) extends DeclarationMsg(OverrideErrorID): + def msg = + val isConcreteOverAbstract = + (other.owner isSubClass member.owner) && other.is(Deferred) && !member.is(Deferred) + def addendum = + if isConcreteOverAbstract then + i"""| + |(Note that ${err.infoStringWithLocation(other, base)} is abstract, + |and is therefore overridden by concrete ${err.infoStringWithLocation(member, base)})""" else "" - def symLocation(sym: Symbol) = { - val lineDesc = - if (sym.span.exists && sym.span != sym.owner.span) - s" at line ${sym.srcPos.line + 1}" - else "" - i"in ${sym.owner}${lineDesc}" - } - val clashDescription = - if (decl.owner eq previousDecl.owner) - "Double definition" - else if ((decl.owner eq base) || (previousDecl eq base)) - "Name clash between defined and inherited member" - else - "Name clash between inherited members" - - atPhase(typerPhase) { - e"""$clashDescription: - |${previousDecl.showDcl} ${symLocation(previousDecl)} and - |${decl.showDcl} ${symLocation(decl)} - |""" - } + details + i"""error overriding ${err.infoStringWithLocation(other, base)}; + | ${err.infoString(member, base, showLocation = member.owner != base.typeSymbol)} $core$addendum""" + override def canExplain = + memberTp.exists && otherTp.exists + def explain = + if canExplain then err.whyNoMatchStr(memberTp, otherTp) else "" + +class ForwardReferenceExtendsOverDefinition(value: Symbol, definition: Symbol)(using Context) +extends ReferenceMsg(ForwardReferenceExtendsOverDefinitionID) { + def msg = e"${definition.name} is a forward reference extending over the definition of ${value.name}" + + def explain = + e"""|${definition.name} is used before you define it, and the definition of ${value.name} + |appears between that use and the definition of ${definition.name}. + | + |Forward references are allowed only, if there are no value definitions between + |the reference and the referred method definition. + | + |Define ${definition.name} before it is used, + |or move the definition of ${value.name} so it does not appear between + |the declaration of ${definition.name} and its use, + |or define ${value.name} as lazy. + |""" +} + +class ExpectedTokenButFound(expected: Token, found: Token)(using Context) +extends SyntaxMsg(ExpectedTokenButFoundID) { + + private def foundText = Tokens.showToken(found) + + def msg = + val expectedText = + if (Tokens.isIdentifier(expected)) "an identifier" + else Tokens.showToken(expected) + e"""${expectedText} expected, but ${foundText} found""" + + def explain = + if (Tokens.isIdentifier(expected) && Tokens.isKeyword(found)) + s""" + |If you want to use $foundText as identifier, you may put it in backticks: `${Tokens.tokenString(found)}`.""".stripMargin + else + "" +} + +class MixedLeftAndRightAssociativeOps(op1: Name, op2: Name, op2LeftAssoc: Boolean)(using Context) +extends SyntaxMsg(MixedLeftAndRightAssociativeOpsID) { + def msg = + val op1Asso: String = if (op2LeftAssoc) "which is right-associative" else "which is left-associative" + val op2Asso: String = if (op2LeftAssoc) "which is left-associative" else "which is right-associative" + e"${op1} (${op1Asso}) and ${op2} ($op2Asso) have same precedence and may not be mixed" + def explain = + s"""|The operators ${op1} and ${op2} are used as infix operators in the same expression, + |but they bind to different sides: + |${op1} is applied to the operand to its ${if (op2LeftAssoc) "right" else "left"} + |${op2} is applied to the operand to its ${if (op2LeftAssoc) "left" else "right"} + |As both have the same precedence the compiler can't decide which to apply first. + | + |You may use parenthesis to make the application order explicit, + |or use method application syntax operand1.${op1}(operand2). + | + |Operators ending in a colon ${hl(":")} are right-associative. All other operators are left-associative. + | + |Infix operator precedence is determined by the operator's first character. Characters are listed + |below in increasing order of precedence, with characters on the same line having the same precedence. + | (all letters) + | | + | ^ + | & + | = ! + | < > + | : + | + - + | * / % + | (all other special characters) + |Operators starting with a letter have lowest precedence, followed by operators starting with `|`, etc. + |""".stripMargin +} + +class CantInstantiateAbstractClassOrTrait(cls: Symbol, isTrait: Boolean)(using Context) +extends TypeMsg(CantInstantiateAbstractClassOrTraitID) { + private val traitOrAbstract = if (isTrait) "a trait" else "abstract" + def msg = e"""${cls.name} is ${traitOrAbstract}; it cannot be instantiated""" + def explain = + e"""|Abstract classes and traits need to be extended by a concrete class or object + |to make their functionality accessible. + | + |You may want to create an anonymous class extending ${cls.name} with + | ${s"class ${cls.name} { }"} + | + |or add a companion object with + | ${s"object ${cls.name} extends ${cls.name}"} + | + |You need to implement any abstract members in both cases. + |""" +} + +class UnreducibleApplication(tycon: Type)(using Context) extends TypeMsg(UnreducibleApplicationID): + def msg = e"unreducible application of higher-kinded type $tycon to wildcard arguments" + def explain = + e"""|An abstract type constructor cannot be applied to wildcard arguments. + |Such applications are equivalent to existential types, which are not + |supported in Scala 3.""" + +class OverloadedOrRecursiveMethodNeedsResultType(cycleSym: Symbol)(using Context) +extends CyclicMsg(OverloadedOrRecursiveMethodNeedsResultTypeID) { + def msg = e"""Overloaded or recursive $cycleSym needs return type""" + def explain = + e"""Case 1: $cycleSym is overloaded + |If there are multiple methods named $cycleSym and at least one definition of + |it calls another, you need to specify the calling method's return type. + | + |Case 2: $cycleSym is recursive + |If $cycleSym calls itself on any path (even through mutual recursion), you need to specify the return type + |of $cycleSym or of a definition it's mutually recursive with. + |""" +} + +class RecursiveValueNeedsResultType(cycleSym: Symbol)(using Context) +extends CyclicMsg(RecursiveValueNeedsResultTypeID) { + def msg = e"""Recursive $cycleSym needs type""" + def explain = + e"""The definition of $cycleSym is recursive and you need to specify its type. + |""" +} + +class CyclicReferenceInvolving(denot: SymDenotation)(using Context) +extends CyclicMsg(CyclicReferenceInvolvingID) { + def msg = + val where = if denot.exists then s" involving $denot" else "" + e"Cyclic reference$where" + def explain = + e"""|$denot is declared as part of a cycle which makes it impossible for the + |compiler to decide upon ${denot.name}'s type. + |To avoid this error, try giving ${denot.name} an explicit type. + |""" +} + +class CyclicReferenceInvolvingImplicit(cycleSym: Symbol)(using Context) +extends CyclicMsg(CyclicReferenceInvolvingImplicitID) { + def msg = e"""Cyclic reference involving implicit $cycleSym""" + def explain = + e"""|$cycleSym is declared as part of a cycle which makes it impossible for the + |compiler to decide upon ${cycleSym.name}'s type. + |This might happen when the right hand-side of $cycleSym's definition involves an implicit search. + |To avoid this error, try giving ${cycleSym.name} an explicit type. + |""" +} + +class SkolemInInferred(tree: tpd.Tree, pt: Type, argument: tpd.Tree)(using Context) +extends TypeMsg(SkolemInInferredID): + def msg = + def argStr = + if argument.isEmpty then "" + else i" from argument of type ${argument.tpe.widen}" + e"""Failure to generate given instance for type $pt$argStr) + | + |I found: $tree + |But the part corresponding to `` is not a reference that can be generated. + |This might be because resolution yielded as given instance a function that is not + |known to be total and side-effect free.""" + def explain = + e"""The part of given resolution that corresponds to `` produced a term that + |is not a stable reference. Therefore a given instance could not be generated. + | + |To trouble-shoot the problem, try to supply an explicit expression instead of + |relying on implicit search at this point.""" + +class SuperQualMustBeParent(qual: untpd.Ident, cls: ClassSymbol)(using Context) +extends ReferenceMsg(SuperQualMustBeParentID) { + def msg = e"""|$qual does not name a parent of $cls""" + def explain = + val parents: Seq[String] = (cls.info.parents map (_.typeSymbol.name.show)).sorted + e"""|When a qualifier ${hl("T")} is used in a ${hl("super")} prefix of the form ${hl("C.super[T]")}, + |${hl("T")} must be a parent type of ${hl("C")}. + | + |In this case, the parents of $cls are: + |${parents.mkString(" - ", "\n - ", "")} + |""" +} + +class VarArgsParamMustComeLast()(using Context) +extends SyntaxMsg(VarArgsParamMustComeLastID) { + def msg = e"""${hl("varargs")} parameter must come last""" + def explain = + e"""|The ${hl("varargs")} field must be the last field in the method signature. + |Attempting to define a field in a method signature after a ${hl("varargs")} field is an error. + |""" +} + +import typer.Typer.BindingPrec + +class AmbiguousReference(name: Name, newPrec: BindingPrec, prevPrec: BindingPrec, prevCtx: Context)(using Context) + extends ReferenceMsg(AmbiguousReferenceID) { + + /** A string which explains how something was bound; Depending on `prec` this is either + * imported by + * or defined in + */ + private def bindingString(prec: BindingPrec, whereFound: Context, qualifier: String = "")(using Context) = { + val howVisible = prec match { + case BindingPrec.Definition => "defined" + case BindingPrec.Inheritance => "inherited" + case BindingPrec.NamedImport => "imported by name" + case BindingPrec.WildImport => "imported" + case BindingPrec.PackageClause => "found" + case BindingPrec.NothingBound => assert(false) } - def explain = "" - } - - class ImportRenamedTwice(ident: untpd.Ident)(using Context) extends SyntaxMsg(ImportRenamedTwiceID) { - def msg = s"${ident.show} is renamed twice on the same import line." - def explain = "" - } - - class TypeTestAlwaysDiverges(scrutTp: Type, testTp: Type)(using Context) extends SyntaxMsg(TypeTestAlwaysDivergesID) { - def msg = - s"This type test will never return a result since the scrutinee type ${scrutTp.show} does not contain any value." - def explain = "" - } - - // Relative of CyclicReferenceInvolvingImplicit and RecursiveValueNeedsResultType - class TermMemberNeedsResultTypeForImplicitSearch(cycleSym: Symbol)(using Context) - extends CyclicMsg(TermMemberNeedsNeedsResultTypeForImplicitSearchID) { - def msg = e"""$cycleSym needs result type because its right-hand side attempts implicit search""" - def explain = - e"""|The right hand-side of $cycleSym's definition requires an implicit search at the highlighted position. - |To avoid this error, give `$cycleSym` an explicit type. - |""" - } - - class ClassCannotExtendEnum(cls: Symbol, parent: Symbol)(using Context) extends SyntaxMsg(ClassCannotExtendEnumID) { - def msg = e"""$cls in ${cls.owner} extends enum ${parent.name}, but extending enums is prohibited.""" - def explain = "" - } - - class NotAnExtractor(tree: untpd.Tree)(using Context) extends SyntaxMsg(NotAnExtractorID) { - def msg = e"$tree cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method" - def explain = - e"""|An ${hl("unapply")} method should be defined in an ${hl("object")} as follow: - | - If it is just a test, return a ${hl("Boolean")}. For example ${hl("case even()")} - | - If it returns a single sub-value of type T, return an ${hl("Option[T]")} - | - If it returns several sub-values T1,...,Tn, group them in an optional tuple ${hl("Option[(T1,...,Tn)]")} - | - |Sometimes, the number of sub-values isn't fixed and we would like to return a sequence. - |For this reason, you can also define patterns through ${hl("unapplySeq")} which returns ${hl("Option[Seq[T]]")}. - |This mechanism is used for instance in pattern ${hl("case List(x1, ..., xn)")}""" - } - - class MemberWithSameNameAsStatic()(using Context) - extends SyntaxMsg(MemberWithSameNameAsStaticID) { - def msg = e"Companion classes cannot define members with same name as a ${hl("@static")} member" - def explain = "" - } - - class PureExpressionInStatementPosition(stat: untpd.Tree, val exprOwner: Symbol)(using Context) - extends Message(PureExpressionInStatementPositionID) { - def kind = MessageKind.PotentialIssue - def msg = "A pure expression does nothing in statement position; you may be omitting necessary parentheses" - def explain = - e"""The pure expression $stat doesn't have any side effect and its result is not assigned elsewhere. - |It can be removed without changing the semantics of the program. This may indicate an error.""" - } + if (prec.isImportPrec) { + ex"""$howVisible$qualifier by ${e"${whereFound.importInfo}"}""" + } else + ex"""$howVisible$qualifier in ${e"${whereFound.owner}"}""" + } + + def msg = + i"""|Reference to ${e"$name"} is ambiguous, + |it is both ${bindingString(newPrec, ctx)} + |and ${bindingString(prevPrec, prevCtx, " subsequently")}""" + + def explain = + e"""|The compiler can't decide which of the possible choices you + |are referencing with $name: A definition of lower precedence + |in an inner scope, or a definition with higher precedence in + |an outer scope. + |Note: + | - Definitions in an enclosing scope take precedence over inherited definitions + | - Definitions take precedence over imports + | - Named imports take precedence over wildcard imports + | - You may replace a name when imported using + | ${hl("import")} scala.{ $name => ${name.show + "Tick"} } + |""" +} + +class MethodDoesNotTakeParameters(tree: tpd.Tree)(using Context) +extends TypeMsg(MethodDoesNotTakeParametersId) { + def methodSymbol(using Context): Symbol = + def recur(t: tpd.Tree): Symbol = + val sym = tpd.methPart(t).symbol + if sym == defn.Any_typeCast then + t match + case TypeApply(Select(qual, _), _) => recur(qual) + case _ => sym + else sym + recur(tree) + + def msg = { + val more = if (tree.isInstanceOf[tpd.Apply]) " more" else "" + val meth = methodSymbol + val methStr = if (meth.exists) meth.showLocated else "expression" + e"$methStr does not take$more parameters" + } + + def explain = { + val isNullary = methodSymbol.info.isInstanceOf[ExprType] + val addendum = + if (isNullary) "\nNullary methods may not be called with parenthesis" + else "" - class TraitCompanionWithMutableStatic()(using Context) - extends SyntaxMsg(TraitCompanionWithMutableStaticID) { - def msg = e"Companion of traits cannot define mutable @static fields" + "You have specified more parameter lists than defined in the method definition(s)." + addendum + } + +} + +class AmbiguousOverload(tree: tpd.Tree, val alternatives: List[SingleDenotation], pt: Type, addendum: String = "")( + implicit ctx: Context) +extends ReferenceMsg(AmbiguousOverloadID) { + private def all = if (alternatives.length == 2) "both" else "all" + def msg = + e"""|Ambiguous overload. The ${err.overloadedAltsStr(alternatives)} + |$all match ${err.expectedTypeStr(pt)}$addendum""" + def explain = + e"""|There are ${alternatives.length} methods that could be referenced as the compiler knows too little + |about the expected type. + |You may specify the expected type e.g. by + |- assigning it to a value with a specified type, or + |- adding a type ascription as in ${hl("instance.myMethod: String => Int")} + |""" +} + +class ReassignmentToVal(name: Name)(using Context) + extends TypeMsg(ReassignmentToValID) { + def msg = e"""Reassignment to val $name""" + def explain = + e"""|You can not assign a new value to $name as values can't be changed. + |Keep in mind that every statement has a value, so you may e.g. use + | ${hl("val")} $name ${hl("= if (condition) 2 else 5")} + |In case you need a reassignable name, you can declare it as + |variable + | ${hl("var")} $name ${hl("=")} ... + |""" +} + +class TypeDoesNotTakeParameters(tpe: Type, params: List[untpd.Tree])(using Context) + extends TypeMsg(TypeDoesNotTakeParametersID) { + private def fboundsAddendum(using Context) = + if tpe.typeSymbol.isAllOf(Provisional | TypeParam) then + "\n(Note that F-bounds of type parameters may not be type lambdas)" + else "" + def msg = e"$tpe does not take type parameters$fboundsAddendum" + def explain = + val ps = + if (params.size == 1) s"a type parameter ${params.head}" + else s"type parameters ${params.map(_.show).mkString(", ")}" + i"""You specified ${NoColor(ps)} for ${e"$tpe"}, which is not + |declared to take any. + |""" +} + +class VarValParametersMayNotBeCallByName(name: TermName, mutable: Boolean)(using Context) + extends SyntaxMsg(VarValParametersMayNotBeCallByNameID) { + def varOrVal = if (mutable) e"${hl("var")}" else e"${hl("val")}" + def msg = s"$varOrVal parameters may not be call-by-name" + def explain = + e"""${hl("var")} and ${hl("val")} parameters of classes and traits may no be call-by-name. In case you + |want the parameter to be evaluated on demand, consider making it just a parameter + |and a ${hl("def")} in the class such as + | ${s"class MyClass(${name}Tick: => String) {"} + | ${s" def $name() = ${name}Tick"} + | ${hl("}")} + |""" +} + +class MissingTypeParameterFor(tpe: Type)(using Context) + extends SyntaxMsg(MissingTypeParameterForID) { + def msg = + if (tpe.derivesFrom(defn.AnyKindClass)) e"${tpe} cannot be used as a value type" + else e"Missing type parameter for ${tpe}" + def explain = "" +} + +class MissingTypeParameterInTypeApp(tpe: Type)(using Context) + extends TypeMsg(MissingTypeParameterInTypeAppID) { + def numParams = tpe.typeParams.length + def parameters = if (numParams == 1) "parameter" else "parameters" + def msg = e"Missing type $parameters for $tpe" + def explain = e"A fully applied type is expected but $tpe takes $numParams $parameters" +} + +class MissingArgument(pname: Name, methString: String)(using Context) + extends TypeMsg(MissingArgumentID): + def msg = + if pname.firstPart contains '$' then s"not enough arguments for $methString" + else s"missing argument for parameter $pname of $methString" + def explain = "" + +class DoesNotConformToBound(tpe: Type, which: String, bound: Type)(using Context) + extends TypeMismatchMsg( + if which == "lower" then bound else tpe, + if which == "lower" then tpe else bound)(DoesNotConformToBoundID): + private def isBounds = tpe match + case TypeBounds(lo, hi) => lo ne hi + case _ => false + override def canExplain = !isBounds + def msg = + if isBounds then + e"Type argument ${tpe} does not overlap with $which bound $bound" + else + e"Type argument ${tpe} does not conform to $which bound $bound" + +class DoesNotConformToSelfType(category: String, selfType: Type, cls: Symbol, + otherSelf: Type, relation: String, other: Symbol)( + implicit ctx: Context) + extends TypeMismatchMsg(selfType, otherSelf)(DoesNotConformToSelfTypeID) { + def msg = e"""$category: self type $selfType of $cls does not conform to self type $otherSelf + |of $relation $other""" +} + +class DoesNotConformToSelfTypeCantBeInstantiated(tp: Type, selfType: Type)( + implicit ctx: Context) + extends TypeMismatchMsg(tp, selfType)(DoesNotConformToSelfTypeCantBeInstantiatedID) { + def msg = e"""$tp does not conform to its self type $selfType; cannot be instantiated""" +} + +class IllegalParameterInit(found: Type, expected: Type, param: Symbol, cls: Symbol)(using Context) + extends TypeMismatchMsg(found, expected)(IllegalParameterInitID): + def msg = + e"""illegal parameter initialization of $param. + | + | The argument passed for $param has type: $found + | but $cls expects $param to have type: $expected""" + +class AbstractMemberMayNotHaveModifier(sym: Symbol, flag: FlagSet)( + implicit ctx: Context) + extends SyntaxMsg(AbstractMemberMayNotHaveModifierID) { + def msg = e"""${hl("abstract")} $sym may not have `${flag.flagsString}` modifier""" + def explain = "" +} + +class TypesAndTraitsCantBeImplicit()(using Context) + extends SyntaxMsg(TypesAndTraitsCantBeImplicitID) { + def msg = e"""${hl("implicit")} modifier cannot be used for types or traits""" + def explain = "" +} + +class OnlyClassesCanBeAbstract(sym: Symbol)( + implicit ctx: Context) + extends SyntaxMsg(OnlyClassesCanBeAbstractID) { + def explain = "" + def msg = e"""${hl("abstract")} modifier can be used only for classes; it should be omitted for abstract members""" +} + +class AbstractOverrideOnlyInTraits(sym: Symbol)( + implicit ctx: Context) + extends SyntaxMsg(AbstractOverrideOnlyInTraitsID) { + def msg = e"""${hl("abstract override")} modifier only allowed for members of traits""" + def explain = "" +} + +class TraitsMayNotBeFinal(sym: Symbol)( + implicit ctx: Context) + extends SyntaxMsg(TraitsMayNotBeFinalID) { + def msg = e"""$sym may not be ${hl("final")}""" + def explain = + "A trait can never be final since it is abstract and must be extended to be useful." +} + +class NativeMembersMayNotHaveImplementation(sym: Symbol)( + implicit ctx: Context) + extends SyntaxMsg(NativeMembersMayNotHaveImplementationID) { + def msg = e"""${hl("@native")} members may not have an implementation""" + def explain = "" +} + +class TraitMayNotDefineNativeMethod(sym: Symbol)( + implicit ctx: Context) + extends SyntaxMsg(TraitMayNotDefineNativeMethodID) { + def msg = e"""A trait cannot define a ${hl("@native")} method.""" + def explain = "" +} + +class OnlyClassesCanHaveDeclaredButUndefinedMembers(sym: Symbol)( + implicit ctx: Context) + extends SyntaxMsg(OnlyClassesCanHaveDeclaredButUndefinedMembersID) { + + def msg = e"""Declaration of $sym not allowed here: only classes can have declared but undefined members""" + def explain = + if sym.is(Mutable) then "Note that variables need to be initialized to be defined." + else "" +} + +class CannotExtendAnyVal(sym: Symbol)(using Context) + extends SyntaxMsg(CannotExtendAnyValID) { + def msg = e"""$sym cannot extend ${hl("AnyVal")}""" + def explain = + e"""Only classes (not traits) are allowed to extend ${hl("AnyVal")}, but traits may extend + |${hl("Any")} to become ${Green("\"universal traits\"")} which may only have ${hl("def")} members. + |Universal traits can be mixed into classes that extend ${hl("AnyVal")}. + |""" +} + +class CannotExtendJavaEnum(sym: Symbol)(using Context) + extends SyntaxMsg(CannotExtendJavaEnumID) { + def msg = e"""$sym cannot extend ${hl("java.lang.Enum")}: only enums defined with the ${hl("enum")} syntax can""" def explain = "" } - class LazyStaticField()(using Context) - extends SyntaxMsg(LazyStaticFieldID) { - def msg = e"Lazy @static fields are not supported" +class CannotExtendContextFunction(sym: Symbol)(using Context) + extends SyntaxMsg(CannotExtendFunctionID) { + def msg = e"""$sym cannot extend a context function class""" def explain = "" } - class StaticOverridingNonStaticMembers()(using Context) - extends SyntaxMsg(StaticOverridingNonStaticMembersID) { - def msg = e"${hl("@static")} members cannot override or implement non-static ones" +class JavaEnumParentArgs(parent: Type)(using Context) + extends TypeMsg(JavaEnumParentArgsID) { + def msg = e"""not enough arguments for constructor Enum: ${hl("(name: String, ordinal: Int)")}: ${hl(parent.show)}""" def explain = "" } - class OverloadInRefinement(rsym: Symbol)(using Context) - extends DeclarationMsg(OverloadInRefinementID) { - def msg = "Refinements cannot introduce overloaded definitions" - def explain = - e"""The refinement `$rsym` introduces an overloaded definition. - |Refinements cannot contain overloaded definitions.""" - } - - class NoMatchingOverload(val alternatives: List[SingleDenotation], pt: Type)(using Context) - extends TypeMsg(NoMatchingOverloadID) { - def msg = - e"""None of the ${err.overloadedAltsStr(alternatives)} - |match ${err.expectedTypeStr(pt)}""" - def explain = "" - } - class StableIdentPattern(tree: untpd.Tree, pt: Type)(using Context) - extends TypeMsg(StableIdentPatternID) { - def msg = - e"""Stable identifier required, but $tree found""" - def explain = "" +class CannotHaveSameNameAs(sym: Symbol, cls: Symbol, reason: CannotHaveSameNameAs.Reason)(using Context) + extends SyntaxMsg(CannotHaveSameNameAsID) { + import CannotHaveSameNameAs._ + def reasonMessage(using Context): String = reason match { + case CannotBeOverridden => "class definitions cannot be overridden" + case DefinedInSelf(self) => + s"""cannot define ${sym.showKind} member with the same name as a ${cls.showKind} member in self reference ${self.name}. + |(Note: this can be resolved by using another name) + |""".stripMargin } - class IllegalSuperAccessor(base: Symbol, memberName: Name, targetName: Name, - acc: Symbol, accTp: Type, - other: Symbol, otherTp: Type)(using Context) extends DeclarationMsg(IllegalSuperAccessorID) { - def msg = { - // The mixin containing a super-call that requires a super-accessor - val accMixin = acc.owner - // The class or trait that the super-accessor should resolve too in `base` - val otherMixin = other.owner - // The super-call in `accMixin` - val superCall = hl(i"super.$memberName") - // The super-call that the super-accesors in `base` forwards to - val resolvedSuperCall = hl(i"super[${otherMixin.name}].$memberName") - // The super-call that we would have called if `super` in traits behaved like it - // does in classes, i.e. followed the linearization of the trait itself. - val staticSuperCall = { - val staticSuper = accMixin.asClass.info.parents.reverse - .find(_.nonPrivateMember(memberName) - .matchingDenotation(accMixin.thisType, acc.info, targetName).exists) - val staticSuperName = staticSuper match { - case Some(parent) => - parent.classSymbol.name.show - case None => // Might be reachable under separate compilation - "SomeParent" + def msg = e"""$sym cannot have the same name as ${cls.showLocated} -- """ + reasonMessage + def explain = "" +} +object CannotHaveSameNameAs { + sealed trait Reason + case object CannotBeOverridden extends Reason + case class DefinedInSelf(self: tpd.ValDef) extends Reason +} + +class ValueClassesMayNotDefineInner(valueClass: Symbol, inner: Symbol)(using Context) + extends SyntaxMsg(ValueClassesMayNotDefineInnerID) { + def msg = e"""Value classes may not define an inner class""" + def explain = "" +} + +class ValueClassesMayNotDefineNonParameterField(valueClass: Symbol, field: Symbol)(using Context) + extends SyntaxMsg(ValueClassesMayNotDefineNonParameterFieldID) { + def msg = e"""Value classes may not define non-parameter field""" + def explain = "" +} + +class ValueClassesMayNotDefineASecondaryConstructor(valueClass: Symbol, constructor: Symbol)(using Context) + extends SyntaxMsg(ValueClassesMayNotDefineASecondaryConstructorID) { + def msg = e"""Value classes may not define a secondary constructor""" + def explain = "" +} + +class ValueClassesMayNotContainInitalization(valueClass: Symbol)(using Context) + extends SyntaxMsg(ValueClassesMayNotContainInitalizationID) { + def msg = e"""Value classes may not contain initialization statements""" + def explain = "" +} + +class ValueClassesMayNotBeAbstract(valueClass: Symbol)(using Context) + extends SyntaxMsg(ValueClassesMayNotBeAbstractID) { + def msg = e"""Value classes may not be ${hl("abstract")}""" + def explain = "" +} + +class ValueClassesMayNotBeContainted(valueClass: Symbol)(using Context) + extends SyntaxMsg(ValueClassesMayNotBeContaintedID) { + private def localOrMember = if (valueClass.owner.isTerm) "local class" else "member of another class" + def msg = s"""Value classes may not be a $localOrMember""" + def explain = "" +} + +class ValueClassesMayNotWrapAnotherValueClass(valueClass: Symbol)(using Context) + extends SyntaxMsg(ValueClassesMayNotWrapAnotherValueClassID) { + def msg = """A value class may not wrap another user-defined value class""" + def explain = "" +} + +class ValueClassParameterMayNotBeAVar(valueClass: Symbol, param: Symbol)(using Context) + extends SyntaxMsg(ValueClassParameterMayNotBeAVarID) { + def msg = e"""A value class parameter may not be a ${hl("var")}""" + def explain = + e"""A value class must have exactly one ${hl("val")} parameter.""" +} + +class ValueClassNeedsOneValParam(valueClass: Symbol)(using Context) + extends SyntaxMsg(ValueClassNeedsExactlyOneValParamID) { + def msg = e"""Value class needs one ${hl("val")} parameter""" + def explain = "" +} + +class ValueClassParameterMayNotBeCallByName(valueClass: Symbol, param: Symbol)(using Context) + extends SyntaxMsg(ValueClassParameterMayNotBeCallByNameID) { + def msg = s"Value class parameter `${param.name}` may not be call-by-name" + def explain = "" +} + +class SuperCallsNotAllowedInlineable(symbol: Symbol)(using Context) + extends SyntaxMsg(SuperCallsNotAllowedInlineableID) { + def msg = e"Super call not allowed in inlineable $symbol" + def explain = "Method inlining prohibits calling superclass methods, as it may lead to confusion about which super is being called." +} + +class NotAPath(tp: Type, usage: String)(using Context) extends TypeMsg(NotAPathID): + def msg = e"$tp is not a valid $usage, since it is not an immutable path" + def explain = + i"""An immutable path is + | - a reference to an immutable value, or + | - a reference to `this`, or + | - a selection of an immutable path with an immutable value.""" + +class WrongNumberOfParameters(expected: Int)(using Context) + extends SyntaxMsg(WrongNumberOfParametersID) { + def msg = s"Wrong number of parameters, expected: $expected" + def explain = "" +} + +class DuplicatePrivateProtectedQualifier()(using Context) + extends SyntaxMsg(DuplicatePrivateProtectedQualifierID) { + def msg = "Duplicate private/protected qualifier" + def explain = + e"It is not allowed to combine `private` and `protected` modifiers even if they are qualified to different scopes" +} + +class ExpectedStartOfTopLevelDefinition()(using Context) + extends SyntaxMsg(ExpectedStartOfTopLevelDefinitionID) { + def msg = "Expected start of definition" + def explain = + e"You have to provide either ${hl("class")}, ${hl("trait")}, ${hl("object")}, or ${hl("enum")} definitions after qualifiers" +} + +class NoReturnFromInlineable(owner: Symbol)(using Context) + extends SyntaxMsg(NoReturnFromInlineableID) { + def msg = e"No explicit ${hl("return")} allowed from inlineable $owner" + def explain = + e"""Methods marked with ${hl("inline")} modifier may not use ${hl("return")} statements. + |Instead, you should rely on the last expression's value being + |returned from a method. + |""" +} + +class ReturnOutsideMethodDefinition(owner: Symbol)(using Context) + extends SyntaxMsg(ReturnOutsideMethodDefinitionID) { + def msg = e"${hl("return")} outside method definition" + def explain = + e"""You used ${hl("return")} in ${owner}. + |${hl("return")} is a keyword and may only be used within method declarations. + |""" +} + +class ExtendFinalClass(clazz:Symbol, finalClazz: Symbol)(using Context) + extends SyntaxMsg(ExtendFinalClassID) { + def msg = e"$clazz cannot extend ${hl("final")} $finalClazz" + def explain = + e"""A class marked with the ${hl("final")} keyword cannot be extended""" +} + +class ExpectedTypeBoundOrEquals(found: Token)(using Context) + extends SyntaxMsg(ExpectedTypeBoundOrEqualsID) { + def msg = e"${hl("=")}, ${hl(">:")}, or ${hl("<:")} expected, but ${Tokens.showToken(found)} found" + + def explain = + e"""Type parameters and abstract types may be constrained by a type bound. + |Such type bounds limit the concrete values of the type variables and possibly + |reveal more information about the members of such types. + | + |A lower type bound ${hl("B >: A")} expresses that the type variable ${hl("B")} + |refers to a supertype of type ${hl("A")}. + | + |An upper type bound ${hl("T <: A")} declares that type variable ${hl("T")} + |refers to a subtype of type ${hl("A")}. + |""" +} + +class ClassAndCompanionNameClash(cls: Symbol, other: Symbol)(using Context) + extends NamingMsg(ClassAndCompanionNameClashID) { + def msg = + val name = cls.name.stripModuleClassSuffix + e"Name clash: both ${cls.owner} and its companion object defines $name" + def explain = + e"""|A ${cls.kindString} and its companion object cannot both define a ${hl("class")}, ${hl("trait")} or ${hl("object")} with the same name: + | - ${cls.owner} defines ${cls} + | - ${other.owner} defines ${other}""" +} + +class TailrecNotApplicable(symbol: Symbol)(using Context) + extends SyntaxMsg(TailrecNotApplicableID) { + def msg = { + val reason = + if (!symbol.is(Method)) e"$symbol isn't a method" + else if (symbol.is(Deferred)) e"$symbol is abstract" + else if (!symbol.isEffectivelyFinal) e"$symbol is neither ${hl("private")} nor ${hl("final")} so can be overridden" + else e"$symbol contains no recursive calls" + + s"TailRec optimisation not applicable, $reason" + } + def explain = "" +} + +class FailureToEliminateExistential(tp: Type, tp1: Type, tp2: Type, boundSyms: List[Symbol], classRoot: Symbol)(using Context) + extends Message(FailureToEliminateExistentialID) { + def kind = MessageKind.Compatibility + def msg = + val originalType = ctx.printer.dclsText(boundSyms, "; ").show + e"""An existential type that came from a Scala-2 classfile for $classRoot + |cannot be mapped accurately to a Scala-3 equivalent. + |original type : $tp forSome ${originalType} + |reduces to : $tp1 + |type used instead: $tp2 + |This choice can cause follow-on type errors or hide type errors. + |Proceed at own risk.""" + def explain = + e"""Existential types in their full generality are no longer supported. + |Scala-3 does applications of class types to wildcard type arguments. + |Other forms of existential types that come from Scala-2 classfiles + |are only approximated in a best-effort way.""" +} + +class OnlyFunctionsCanBeFollowedByUnderscore(tp: Type)(using Context) + extends SyntaxMsg(OnlyFunctionsCanBeFollowedByUnderscoreID) { + def msg = e"Only function types can be followed by ${hl("_")} but the current expression has type $tp" + def explain = + e"""The syntax ${hl("x _")} is no longer supported if ${hl("x")} is not a function. + |To convert to a function value, you need to explicitly write ${hl("() => x")}""" +} + +class MissingEmptyArgumentList(method: String)(using Context) + extends SyntaxMsg(MissingEmptyArgumentListID) { + def msg = e"$method must be called with ${hl("()")} argument" + def explain = { + val codeExample = + """def next(): T = ... + |next // is expanded to next()""" + + e"""Previously an empty argument list () was implicitly inserted when calling a nullary method without arguments. E.g. + | + |$codeExample + | + |In Dotty, this idiom is an error. The application syntax has to follow exactly the parameter syntax. + |Excluded from this rule are methods that are defined in Java or that override methods defined in Java.""" + } +} + +class DuplicateNamedTypeParameter(name: Name)(using Context) + extends SyntaxMsg(DuplicateNamedTypeParameterID) { + def msg = e"Type parameter $name was defined multiple times." + def explain = "" +} + +class UndefinedNamedTypeParameter(undefinedName: Name, definedNames: List[Name])(using Context) + extends SyntaxMsg(UndefinedNamedTypeParameterID) { + def msg = e"Type parameter $undefinedName is undefined. Expected one of ${definedNames.map(_.show).mkString(", ")}." + def explain = "" +} + +class IllegalStartOfStatement(what: String, isModifier: Boolean, isStat: Boolean)(using Context) extends SyntaxMsg(IllegalStartOfStatementID) { + def msg = + if isStat then + "this kind of statement is not allowed here" + else + val addendum = if isModifier then ": this modifier is not allowed here" else "" + s"Illegal start of $what$addendum" + def explain = + i"""A statement is an import or export, a definition or an expression. + |Some statements are only allowed in certain contexts""" +} + +class TraitIsExpected(symbol: Symbol)(using Context) extends SyntaxMsg(TraitIsExpectedID) { + def msg = e"$symbol is not a trait" + def explain = { + val errorCodeExample = + """class A + |class B + | + |val a = new A with B // will fail with a compile error - class B is not a trait""".stripMargin + val codeExample = + """class A + |trait B + | + |val a = new A with B // compiles normally""".stripMargin + + e"""Only traits can be mixed into classes using a ${hl("with")} keyword. + |Consider the following example: + | + |$errorCodeExample + | + |The example mentioned above would fail because B is not a trait. + |But if you make B a trait it will be compiled without any errors: + | + |$codeExample + |""" + } +} + +class TraitRedefinedFinalMethodFromAnyRef(method: Symbol)(using Context) extends SyntaxMsg(TraitRedefinedFinalMethodFromAnyRefID) { + def msg = e"Traits cannot redefine final $method from ${hl("class AnyRef")}." + def explain = "" +} + +class AlreadyDefined(name: Name, owner: Symbol, conflicting: Symbol)(using Context) extends NamingMsg(AlreadyDefinedID): + def msg = + def where: String = + if conflicting.effectiveOwner.is(Package) && conflicting.associatedFile != null then + i" in ${conflicting.associatedFile}" + else if conflicting.owner == owner then "" + else i" in ${conflicting.owner}" + def note = + if owner.is(Method) || conflicting.is(Method) then + "\n\nNote that overloaded methods must all be defined in the same group of toplevel definitions" + else "" + if conflicting.isTerm != name.isTermName then + e"$name clashes with $conflicting$where; the two must be defined together" + else + e"$name is already defined as $conflicting$where$note" + def explain = "" + +class PackageNameAlreadyDefined(pkg: Symbol)(using Context) extends NamingMsg(PackageNameAlreadyDefinedID) { + def msg = + def where = if pkg.associatedFile == null then "" else s" in ${pkg.associatedFile}" + e"""${pkg.name} is the name of $pkg$where. + |It cannot be used at the same time as the name of a package.""" + def explain = + def or = if pkg.associatedFile == null then "" else " or delete the containing class file" + e"""An ${hl("object")} or other toplevel definition cannot have the same name as an existing ${hl("package")}. + |Rename either one of them$or.""" +} + +class UnapplyInvalidNumberOfArguments(qual: untpd.Tree, argTypes: List[Type])(using Context) + extends SyntaxMsg(UnapplyInvalidNumberOfArgumentsID) { + def msg = e"Wrong number of argument patterns for $qual; expected: ($argTypes%, %)" + def explain = + e"""The Unapply method of $qual was used with incorrect number of arguments. + |Expected usage would be something like: + |case $qual(${argTypes.map(_ => '_')}%, %) => ... + | + |where subsequent arguments would have following types: ($argTypes%, %). + |""" +} + +class UnapplyInvalidReturnType(unapplyResult: Type, unapplyName: Name)(using Context) + extends DeclarationMsg(UnapplyInvalidReturnTypeID) { + def msg = + val addendum = + if Feature.migrateTo3 && unapplyName == nme.unapplySeq + then "\nYou might want to try to rewrite the extractor to use `unapply` instead." + else "" + e"""| ${Red(i"$unapplyResult")} is not a valid result type of an $unapplyName method of an ${Magenta("extractor")}.$addendum""" + def explain = if (unapplyName.show == "unapply") + e""" + |To be used as an extractor, an unapply method has to return a type that either: + | - has members ${Magenta("isEmpty: Boolean")} and ${Magenta("get: S")} (usually an ${Green("Option[S]")}) + | - is a ${Green("Boolean")} + | - is a ${Green("Product")} (like a ${Magenta("Tuple2[T1, T2]")}) + | + |class A(val i: Int) + | + |object B { + | def unapply(a: A): ${Green("Option[Int]")} = Some(a.i) + |} + | + |object C { + | def unapply(a: A): ${Green("Boolean")} = a.i == 2 + |} + | + |object D { + | def unapply(a: A): ${Green("(Int, Int)")} = (a.i, a.i) + |} + | + |object Test { + | def test(a: A) = a match { + | ${Magenta("case B(1)")} => 1 + | ${Magenta("case a @ C()")} => 2 + | ${Magenta("case D(3, 3)")} => 3 + | } + |} + """ + else + e""" + |To be used as an extractor, an unapplySeq method has to return a type which has members + |${Magenta("isEmpty: Boolean")} and ${Magenta("get: S")} where ${Magenta("S <: Seq[V]")} (usually an ${Green("Option[Seq[V]]")}): + | + |object CharList { + | def unapplySeq(s: String): ${Green("Option[Seq[Char]")} = Some(s.toList) + | + | "example" match { + | ${Magenta("case CharList(c1, c2, c3, c4, _, _, _)")} => + | println(s"$$c1,$$c2,$$c3,$$c4") + | case _ => + | println("Expected *exactly* 7 characters!") + | } + |} + """ +} + +class StaticFieldsOnlyAllowedInObjects(member: Symbol)(using Context) extends SyntaxMsg(StaticFieldsOnlyAllowedInObjectsID) { + def msg = e"${hl("@static")} $member in ${member.owner} must be defined inside a static ${hl("object")}." + def explain = + e"${hl("@static")} members are only allowed inside objects." +} + +class StaticFieldsShouldPrecedeNonStatic(member: Symbol, defns: List[tpd.Tree])(using Context) extends SyntaxMsg(StaticFieldsShouldPrecedeNonStaticID) { + def msg = e"${hl("@static")} $member in ${member.owner} must be defined before non-static fields." + def explain = { + val nonStatics = defns.takeWhile(_.symbol != member).take(3).filter(_.isInstanceOf[tpd.ValDef]) + val codeExample = s"""object ${member.owner.name.firstPart} { + | @static ${member} = ... + | ${nonStatics.map(m => s"${m.symbol} = ...").mkString("\n ")} + | ... + |}""" + e"""The fields annotated with @static should precede any non @static fields. + |This ensures that we do not introduce surprises for users in initialization order of this class. + |Static field are initialized when class loading the code of Foo. + |Non static fields are only initialized the first time that Foo is accessed. + | + |The definition of ${member.name} should have been before the non ${hl("@static val")}s: + |$codeExample + |""" + } +} + +class CyclicInheritance(symbol: Symbol, addendum: => String)(using Context) extends SyntaxMsg(CyclicInheritanceID) { + def msg = e"Cyclic inheritance: $symbol extends itself$addendum" + def explain = { + val codeExample = "class A extends A" + + e"""Cyclic inheritance is prohibited in Dotty. + |Consider the following example: + | + |$codeExample + | + |The example mentioned above would fail because this type of inheritance hierarchy + |creates a "cycle" where a not yet defined class A extends itself which makes + |impossible to instantiate an object of this class""" + } +} + +class BadSymbolicReference(denot: SymDenotation)(using Context) +extends ReferenceMsg(BadSymbolicReferenceID) { + def msg = { + val denotationOwner = denot.owner + val denotationName = ctx.fresh.setSetting(ctx.settings.YdebugNames, true).printer.nameString(denot.name) + val file = denot.symbol.associatedFile + val (location, src) = + if (file != null) (s" in $file", file.toString) + else ("", "the signature") + + e"""Bad symbolic reference. A signature$location + |refers to $denotationName in ${denotationOwner.showKind} ${denotationOwner.showFullName} which is not available. + |It may be completely missing from the current classpath, or the version on + |the classpath might be incompatible with the version used when compiling $src.""" + } + + def explain = "" +} + +class UnableToExtendSealedClass(pclazz: Symbol)(using Context) extends SyntaxMsg(UnableToExtendSealedClassID) { + def msg = e"Cannot extend ${hl("sealed")} $pclazz in a different source file" + def explain = "A sealed class or trait can only be extended in the same file as its declaration" +} + +class SymbolHasUnparsableVersionNumber(symbol: Symbol, errorMessage: String)(using Context) +extends SyntaxMsg(SymbolHasUnparsableVersionNumberID) { + def msg = e"${symbol.showLocated} has an unparsable version number: $errorMessage" + def explain = + e"""The ${symbol.showLocated} is marked with ${hl("@migration")} indicating it has changed semantics + |between versions and the ${hl("-Xmigration")} settings is used to warn about constructs + |whose behavior may have changed since version change.""" +} + +class SymbolChangedSemanticsInVersion( + symbol: Symbol, + migrationVersion: ScalaVersion, + migrationMessage: String +)(using Context) extends SyntaxMsg(SymbolChangedSemanticsInVersionID) { + def msg = e"${symbol.showLocated} has changed semantics in version $migrationVersion: $migrationMessage" + def explain = + e"""The ${symbol.showLocated} is marked with ${hl("@migration")} indicating it has changed semantics + |between versions and the ${hl("-Xmigration")} settings is used to warn about constructs + |whose behavior may have changed since version change.""" +} + +class UnableToEmitSwitch()(using Context) +extends SyntaxMsg(UnableToEmitSwitchID) { + def msg = e"Could not emit switch for ${hl("@switch")} annotated match" + def explain = { + val codeExample = + """val ConstantB = 'B' + |final val ConstantC = 'C' + |def tokenMe(ch: Char) = (ch: @switch) match { + | case '\t' | '\n' => 1 + | case 'A' => 2 + | case ConstantB => 3 // a non-literal may prevent switch generation: this would not compile + | case ConstantC => 4 // a constant value is allowed + | case _ => 5 + |}""".stripMargin + + e"""If annotated with ${hl("@switch")}, the compiler will verify that the match has been compiled to a + |tableswitch or lookupswitch and issue an error if it instead compiles into a series of conditional + |expressions. Example usage: + | + |$codeExample + | + |The compiler will not apply the optimisation if: + |- the matched value is not of type ${hl("Int")}, ${hl("Byte")}, ${hl("Short")} or ${hl("Char")} + |- the matched value is not a constant literal + |- there are less than three cases""" + } +} + +class MissingCompanionForStatic(member: Symbol)(using Context) +extends SyntaxMsg(MissingCompanionForStaticID) { + def msg = e"${member.owner} does not have a companion class" + def explain = + e"An object that contains ${hl("@static")} members must have a companion class." +} + +class PolymorphicMethodMissingTypeInParent(rsym: Symbol, parentSym: Symbol)(using Context) +extends SyntaxMsg(PolymorphicMethodMissingTypeInParentID) { + def msg = e"Polymorphic refinement $rsym without matching type in parent $parentSym is no longer allowed" + def explain = + e"""Polymorphic $rsym is not allowed in the structural refinement of $parentSym because + |$rsym does not override any method in $parentSym. Structural refinement does not allow for + |polymorphic methods.""" +} + +class ParamsNoInline(owner: Symbol)(using Context) + extends SyntaxMsg(ParamsNoInlineID) { + def msg = e"""${hl("inline")} modifier can only be used for parameters of inline methods""" + def explain = "" +} + +class JavaSymbolIsNotAValue(symbol: Symbol)(using Context) extends TypeMsg(JavaSymbolIsNotAValueID) { + def msg = { + val kind = + if (symbol is Package) e"$symbol" + else e"Java defined ${hl("class " + symbol.name)}" + + s"$kind is not a value" + } + def explain = "" +} + +class DoubleDefinition(decl: Symbol, previousDecl: Symbol, base: Symbol)(using Context) extends NamingMsg(DoubleDefinitionID) { + def msg = { + def nameAnd = if (decl.name != previousDecl.name) " name and" else "" + def erasedType = if ctx.erasedTypes then i" ${decl.info}" else "" + def details(using Context): String = + if (decl.isRealMethod && previousDecl.isRealMethod) { + import Signature.MatchDegree._ + + // compare the signatures when both symbols represent methods + decl.signature.matchDegree(previousDecl.signature) match { + case NoMatch => + // If the signatures don't match at all at the current phase, then + // they might match after erasure. + if ctx.phase.id <= elimErasedValueTypePhase.id then + atPhase(elimErasedValueTypePhase.next)(details) + else + "" // shouldn't be reachable + case ParamMatch => + "have matching parameter types." + case MethodNotAMethodMatch => + "neither has parameters." + case FullMatch => + val hint = + if !decl.hasAnnotation(defn.TargetNameAnnot) + && !previousDecl.hasAnnotation(defn.TargetNameAnnot) + then + i""" + | + |Consider adding a @targetName annotation to one of the conflicting definitions + |for disambiguation.""" + else "" + i"have the same$nameAnd type$erasedType after erasure.$hint" } - hl(i"super[$staticSuperName].$memberName") } - ex"""$base cannot be defined due to a conflict between its parents when - |implementing a super-accessor for $memberName in $accMixin: - | - |1. One of its parent (${accMixin.name}) contains a call $superCall in its body, - | and when a super-call in a trait is written without an explicit parent - | listed in brackets, it is implemented by a generated super-accessor in - | the class that extends this trait based on the linearization order of - | the class. - |2. Because ${otherMixin.name} comes before ${accMixin.name} in the linearization - | order of ${base.name}, and because ${otherMixin.name} overrides $memberName, - | the super-accessor in ${base.name} is implemented as a call to - | $resolvedSuperCall. - |3. However, - | ${otherTp.widenExpr} (the type of $resolvedSuperCall in ${base.name}) - | is not a subtype of - | ${accTp.widenExpr} (the type of $memberName in $accMixin). - | Hence, the super-accessor that needs to be generated in ${base.name} - | is illegal. - | - |Here are two possible ways to resolve this: - | - |1. Change the linearization order of ${base.name} such that - | ${accMixin.name} comes before ${otherMixin.name}. - |2. Alternatively, replace $superCall in the body of $accMixin by a - | super-call to a specific parent, e.g. $staticSuperCall - |""" + else "" + def symLocation(sym: Symbol) = { + val lineDesc = + if (sym.span.exists && sym.span != sym.owner.span) + s" at line ${sym.srcPos.line + 1}" + else "" + i"in ${sym.owner}${lineDesc}" } - def explain = "" - } + val clashDescription = + if (decl.owner eq previousDecl.owner) + "Double definition" + else if ((decl.owner eq base) || (previousDecl eq base)) + "Name clash between defined and inherited member" + else + "Name clash between inherited members" - class TraitParameterUsedAsParentPrefix(cls: Symbol)(using Context) - extends DeclarationMsg(TraitParameterUsedAsParentPrefixID) { - def msg = - s"${cls.show} cannot extend from a parent that is derived via its own parameters" - def explain = - ex""" - |The parent class/trait that ${cls.show} extends from is obtained from - |the parameter of ${cls.show}. This is disallowed in order to prevent - |outer-related Null Pointer Exceptions in Scala. - | - |In order to fix this issue consider directly extending from the parent rather - |than obtaining it from the parameters of ${cls.show}. + atPhase(typerPhase) { + e"""$clashDescription: + |${previousDecl.showDcl} ${symLocation(previousDecl)} and + |${decl.showDcl} ${symLocation(decl)} |""" - } - - class UnknownNamedEnclosingClassOrObject(name: TypeName)(using Context) - extends ReferenceMsg(UnknownNamedEnclosingClassOrObjectID) { - def msg = - e"""no enclosing class or object is named '${hl(name.show)}'""" - def explain = - ex""" - |The class or object named '${hl(name.show)}' was used as a visibility - |modifier, but could not be resolved. Make sure that - |'${hl(name.show)}' is not misspelled and has been imported into the - |current scope. - """ + } + details + } + def explain = "" +} + +class ImportRenamedTwice(ident: untpd.Ident)(using Context) extends SyntaxMsg(ImportRenamedTwiceID) { + def msg = s"${ident.show} is renamed twice on the same import line." + def explain = "" +} + +class TypeTestAlwaysDiverges(scrutTp: Type, testTp: Type)(using Context) extends SyntaxMsg(TypeTestAlwaysDivergesID) { + def msg = + s"This type test will never return a result since the scrutinee type ${scrutTp.show} does not contain any value." + def explain = "" +} + +// Relative of CyclicReferenceInvolvingImplicit and RecursiveValueNeedsResultType +class TermMemberNeedsResultTypeForImplicitSearch(cycleSym: Symbol)(using Context) + extends CyclicMsg(TermMemberNeedsNeedsResultTypeForImplicitSearchID) { + def msg = e"""$cycleSym needs result type because its right-hand side attempts implicit search""" + def explain = + e"""|The right hand-side of $cycleSym's definition requires an implicit search at the highlighted position. + |To avoid this error, give `$cycleSym` an explicit type. + |""" +} + +class ClassCannotExtendEnum(cls: Symbol, parent: Symbol)(using Context) extends SyntaxMsg(ClassCannotExtendEnumID) { + def msg = e"""$cls in ${cls.owner} extends enum ${parent.name}, but extending enums is prohibited.""" + def explain = "" +} + +class NotAnExtractor(tree: untpd.Tree)(using Context) extends SyntaxMsg(NotAnExtractorID) { + def msg = e"$tree cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method" + def explain = + e"""|An ${hl("unapply")} method should be defined in an ${hl("object")} as follow: + | - If it is just a test, return a ${hl("Boolean")}. For example ${hl("case even()")} + | - If it returns a single sub-value of type T, return an ${hl("Option[T]")} + | - If it returns several sub-values T1,...,Tn, group them in an optional tuple ${hl("Option[(T1,...,Tn)]")} + | + |Sometimes, the number of sub-values isn't fixed and we would like to return a sequence. + |For this reason, you can also define patterns through ${hl("unapplySeq")} which returns ${hl("Option[Seq[T]]")}. + |This mechanism is used for instance in pattern ${hl("case List(x1, ..., xn)")}""" +} + +class MemberWithSameNameAsStatic()(using Context) + extends SyntaxMsg(MemberWithSameNameAsStaticID) { + def msg = e"Companion classes cannot define members with same name as a ${hl("@static")} member" + def explain = "" +} + +class PureExpressionInStatementPosition(stat: untpd.Tree, val exprOwner: Symbol)(using Context) + extends Message(PureExpressionInStatementPositionID) { + def kind = MessageKind.PotentialIssue + def msg = "A pure expression does nothing in statement position; you may be omitting necessary parentheses" + def explain = + e"""The pure expression $stat doesn't have any side effect and its result is not assigned elsewhere. + |It can be removed without changing the semantics of the program. This may indicate an error.""" +} + +class TraitCompanionWithMutableStatic()(using Context) + extends SyntaxMsg(TraitCompanionWithMutableStaticID) { + def msg = e"Companion of traits cannot define mutable @static fields" + def explain = "" +} + +class LazyStaticField()(using Context) + extends SyntaxMsg(LazyStaticFieldID) { + def msg = e"Lazy @static fields are not supported" + def explain = "" +} + +class StaticOverridingNonStaticMembers()(using Context) + extends SyntaxMsg(StaticOverridingNonStaticMembersID) { + def msg = e"${hl("@static")} members cannot override or implement non-static ones" + def explain = "" +} + +class OverloadInRefinement(rsym: Symbol)(using Context) + extends DeclarationMsg(OverloadInRefinementID) { + def msg = "Refinements cannot introduce overloaded definitions" + def explain = + e"""The refinement `$rsym` introduces an overloaded definition. + |Refinements cannot contain overloaded definitions.""" +} + +class NoMatchingOverload(val alternatives: List[SingleDenotation], pt: Type)(using Context) + extends TypeMsg(NoMatchingOverloadID) { + def msg = + e"""None of the ${err.overloadedAltsStr(alternatives)} + |match ${err.expectedTypeStr(pt)}""" + def explain = "" +} +class StableIdentPattern(tree: untpd.Tree, pt: Type)(using Context) + extends TypeMsg(StableIdentPatternID) { + def msg = + e"""Stable identifier required, but $tree found""" + def explain = "" +} + +class IllegalSuperAccessor(base: Symbol, memberName: Name, targetName: Name, + acc: Symbol, accTp: Type, + other: Symbol, otherTp: Type)(using Context) extends DeclarationMsg(IllegalSuperAccessorID) { + def msg = { + // The mixin containing a super-call that requires a super-accessor + val accMixin = acc.owner + // The class or trait that the super-accessor should resolve too in `base` + val otherMixin = other.owner + // The super-call in `accMixin` + val superCall = hl(i"super.$memberName") + // The super-call that the super-accesors in `base` forwards to + val resolvedSuperCall = hl(i"super[${otherMixin.name}].$memberName") + // The super-call that we would have called if `super` in traits behaved like it + // does in classes, i.e. followed the linearization of the trait itself. + val staticSuperCall = { + val staticSuper = accMixin.asClass.info.parents.reverse + .find(_.nonPrivateMember(memberName) + .matchingDenotation(accMixin.thisType, acc.info, targetName).exists) + val staticSuperName = staticSuper match { + case Some(parent) => + parent.classSymbol.name.show + case None => // Might be reachable under separate compilation + "SomeParent" + } + hl(i"super[$staticSuperName].$memberName") } + ex"""$base cannot be defined due to a conflict between its parents when + |implementing a super-accessor for $memberName in $accMixin: + | + |1. One of its parent (${accMixin.name}) contains a call $superCall in its body, + | and when a super-call in a trait is written without an explicit parent + | listed in brackets, it is implemented by a generated super-accessor in + | the class that extends this trait based on the linearization order of + | the class. + |2. Because ${otherMixin.name} comes before ${accMixin.name} in the linearization + | order of ${base.name}, and because ${otherMixin.name} overrides $memberName, + | the super-accessor in ${base.name} is implemented as a call to + | $resolvedSuperCall. + |3. However, + | ${otherTp.widenExpr} (the type of $resolvedSuperCall in ${base.name}) + | is not a subtype of + | ${accTp.widenExpr} (the type of $memberName in $accMixin). + | Hence, the super-accessor that needs to be generated in ${base.name} + | is illegal. + | + |Here are two possible ways to resolve this: + | + |1. Change the linearization order of ${base.name} such that + | ${accMixin.name} comes before ${otherMixin.name}. + |2. Alternatively, replace $superCall in the body of $accMixin by a + | super-call to a specific parent, e.g. $staticSuperCall + |""" + } + def explain = "" +} + +class TraitParameterUsedAsParentPrefix(cls: Symbol)(using Context) + extends DeclarationMsg(TraitParameterUsedAsParentPrefixID) { + def msg = + s"${cls.show} cannot extend from a parent that is derived via its own parameters" + def explain = + ex""" + |The parent class/trait that ${cls.show} extends from is obtained from + |the parameter of ${cls.show}. This is disallowed in order to prevent + |outer-related Null Pointer Exceptions in Scala. + | + |In order to fix this issue consider directly extending from the parent rather + |than obtaining it from the parameters of ${cls.show}. + |""" +} + +class UnknownNamedEnclosingClassOrObject(name: TypeName)(using Context) + extends ReferenceMsg(UnknownNamedEnclosingClassOrObjectID) { + def msg = + e"""no enclosing class or object is named '${hl(name.show)}'""" + def explain = + ex""" + |The class or object named '${hl(name.show)}' was used as a visibility + |modifier, but could not be resolved. Make sure that + |'${hl(name.show)}' is not misspelled and has been imported into the + |current scope. + """ + } + +class IllegalCyclicTypeReference(sym: Symbol, where: String, lastChecked: Type)(using Context) + extends CyclicMsg(IllegalCyclicTypeReferenceID) { + def msg = + val lastCheckedStr = + try lastChecked.show + catch case ex: CyclicReference => "..." + i"illegal cyclic type reference: ${where} ${hl(lastCheckedStr)} of $sym refers back to the type itself" + def explain = "" +} + +class ErasedTypesCanOnlyBeFunctionTypes()(using Context) + extends SyntaxMsg(ErasedTypesCanOnlyBeFunctionTypesID) { + def msg = "Types with erased keyword can only be function types `(erased ...) => ...`" + def explain = "" +} + +class CaseClassMissingNonImplicitParamList(cdef: untpd.TypeDef)(using Context) + extends SyntaxMsg(CaseClassMissingNonImplicitParamListID) { + def msg = + e"""|A ${hl("case class")} must have at least one leading non-implicit parameter list""" + + def explain = + e"""|${cdef.name} must have at least one leading non-implicit parameter list, + | if you're aiming to have a case class parametrized only by implicit ones, you should + | add an explicit ${hl("()")} as the first parameter list to ${cdef.name}.""" +} + +class EnumerationsShouldNotBeEmpty(cdef: untpd.TypeDef)(using Context) + extends SyntaxMsg(EnumerationsShouldNotBeEmptyID) { + def msg = "Enumerations must contain at least one case" + + def explain = + e"""|Enumeration ${cdef.name} must contain at least one case + |Example Usage: + | ${hl("enum")} ${cdef.name} { + | ${hl("case")} Option1, Option2 + | } + |""" +} + +class TypedCaseDoesNotExplicitlyExtendTypedEnum(enumDef: Symbol, caseDef: untpd.TypeDef)(using Context) + extends SyntaxMsg(TypedCaseDoesNotExplicitlyExtendTypedEnumID) { + def msg = i"explicit extends clause needed because both enum case and enum class have type parameters" + + def explain = + e"""Enumerations where the enum class as well as the enum case have type parameters need + |an explicit extends. + |for example: + | ${hl("enum")} ${enumDef.name}[T] { + | ${hl("case")} ${caseDef.name}[U](u: U) ${hl("extends")} ${enumDef.name}[U] + | } + |""" +} + +class IllegalRedefinitionOfStandardKind(kindType: String, name: Name)(using Context) + extends SyntaxMsg(IllegalRedefinitionOfStandardKindID) { + def msg = e"illegal redefinition of standard $kindType $name" + def explain = + e"""| "$name" is a standard Scala core `$kindType` + | Please choose a different name to avoid conflicts + |""" +} + +class NoExtensionMethodAllowed(mdef: untpd.DefDef)(using Context) + extends SyntaxMsg(NoExtensionMethodAllowedID) { + def msg = e"No extension method allowed here, since collective parameters are given" + def explain = + e"""|Extension method: + | `${mdef}` + |is defined inside an extension clause which has collective parameters. + |""" +} + +class ExtensionMethodCannotHaveTypeParams(mdef: untpd.DefDef)(using Context) + extends SyntaxMsg(ExtensionMethodCannotHaveTypeParamsID) { + def msg = i"Extension method cannot have type parameters since some were already given previously" + + def explain = + e"""|Extension method: + | `${mdef}` + |has type parameters `[${mdef.leadingTypeParams.map(_.show).mkString(",")}]`, while the extension clause has + |it's own type parameters. Please consider moving these to the extension clause's type parameter list. + |""" +} + +class ExtensionCanOnlyHaveDefs(mdef: untpd.Tree)(using Context) + extends SyntaxMsg(ExtensionCanOnlyHaveDefsID) { + def msg = e"Only methods allowed here, since collective parameters are given" + def explain = + e"""Extension clauses can only have `def`s + | `${mdef.show}` is not a valid expression here. + |""" +} + +class UnexpectedPatternForSummonFrom(tree: Tree[_])(using Context) + extends SyntaxMsg(UnexpectedPatternForSummonFromID) { + def msg = e"Unexpected pattern for summonFrom. Expected ${hl("`x: T`")} or ${hl("`_`")}" + def explain = + e"""|The pattern "${tree.show}" provided in the ${hl("case")} expression of the ${hl("summonFrom")}, + | needs to be of the form ${hl("`x: T`")} or ${hl("`_`")}. + | + | Example usage: + | inline def a = summonFrom { + | case x: T => ??? + | } + | + | or + | inline def a = summonFrom { + | case _ => ??? + | } + |""" +} + +class AnonymousInstanceCannotBeEmpty(impl: untpd.Template)(using Context) + extends SyntaxMsg(AnonymousInstanceCannotBeEmptyID) { + def msg = i"anonymous instance must implement a type or have at least one extension method" + def explain = + e"""|Anonymous instances cannot be defined with an empty body. The block + |`${impl.show}` should either contain an implemented type or at least one extension method. + |""" +} + +class ModifierNotAllowedForDefinition(flag: Flag)(using Context) + extends SyntaxMsg(ModifierNotAllowedForDefinitionID) { + def msg = e"Modifier ${hl(flag.flagsString)} is not allowed for this definition" + def explain = "" +} + +class RedundantModifier(flag: Flag)(using Context) + extends SyntaxMsg(RedundantModifierID) { + def msg = e"Modifier ${hl(flag.flagsString)} is redundant for this definition" + def explain = "" +} + +class InvalidReferenceInImplicitNotFoundAnnotation(typeVar: String, owner: String)(using Context) + extends ReferenceMsg(InvalidReferenceInImplicitNotFoundAnnotationID) { + def msg = e"""|Invalid reference to a type variable ${hl(typeVar)} found in the annotation argument. + |The variable does not occur as a parameter in the scope of ${hl(owner)}. + |""" + def explain = "" +} + +class CaseClassInInlinedCode(tree: tpd.Tree)(using Context) + extends SyntaxMsg(CaseClassInInlinedCodeID) { + + def defKind = if tree.symbol.is(Module) then "object" else "class" + def msg = s"Case $defKind definitions are not allowed in inline methods or quoted code. Use a normal $defKind instead." + def explain = + e"""Case class/object definitions generate a considerable footprint in code size. + |Inlining such definition would multiply this footprint for each call site. + |""" +} + +class ImplicitSearchTooLargeWarning(limit: Int, openSearchPairs: List[(Candidate, Type)])(using Context) + extends TypeMsg(ImplicitSearchTooLargeID): + override def showAlways = true + def showQuery(query: (Candidate, Type))(using Context): String = + i" ${query._1.ref.symbol.showLocated} for ${query._2}}" + def msg = + e"""Implicit search problem too large. + |an implicit search was terminated with failure after trying $limit expressions. + |The root candidate for the search was: + | + |${showQuery(openSearchPairs.last)} + | + |You can change the behavior by setting the `-Ximplicit-search-limit` value. + |Smaller values cause the search to fail faster. + |Larger values might make a very large search problem succeed. + |""" + def explain = + e"""The overflow happened with the following lists of tried expressions and target types, + |starting with the root query: + | + |${openSearchPairs.reverse.map(showQuery)}%\n% + """ - class IllegalCyclicTypeReference(sym: Symbol, where: String, lastChecked: Type)(using Context) - extends CyclicMsg(IllegalCyclicTypeReferenceID) { - def msg = - val lastCheckedStr = - try lastChecked.show - catch case ex: CyclicReference => "..." - i"illegal cyclic type reference: ${where} ${hl(lastCheckedStr)} of $sym refers back to the type itself" - def explain = "" - } - - class ErasedTypesCanOnlyBeFunctionTypes()(using Context) - extends SyntaxMsg(ErasedTypesCanOnlyBeFunctionTypesID) { - def msg = "Types with erased keyword can only be function types `(erased ...) => ...`" - def explain = "" - } - - class CaseClassMissingNonImplicitParamList(cdef: untpd.TypeDef)(using Context) - extends SyntaxMsg(CaseClassMissingNonImplicitParamListID) { - def msg = - e"""|A ${hl("case class")} must have at least one leading non-implicit parameter list""" - - def explain = - e"""|${cdef.name} must have at least one leading non-implicit parameter list, - | if you're aiming to have a case class parametrized only by implicit ones, you should - | add an explicit ${hl("()")} as the first parameter list to ${cdef.name}.""" - } - - class EnumerationsShouldNotBeEmpty(cdef: untpd.TypeDef)(using Context) - extends SyntaxMsg(EnumerationsShouldNotBeEmptyID) { - def msg = "Enumerations must contain at least one case" - - def explain = - e"""|Enumeration ${cdef.name} must contain at least one case - |Example Usage: - | ${hl("enum")} ${cdef.name} { - | ${hl("case")} Option1, Option2 - | } - |""" - } - - class TypedCaseDoesNotExplicitlyExtendTypedEnum(enumDef: Symbol, caseDef: untpd.TypeDef)(using Context) - extends SyntaxMsg(TypedCaseDoesNotExplicitlyExtendTypedEnumID) { - def msg = i"explicit extends clause needed because both enum case and enum class have type parameters" - - def explain = - e"""Enumerations where the enum class as well as the enum case have type parameters need - |an explicit extends. - |for example: - | ${hl("enum")} ${enumDef.name}[T] { - | ${hl("case")} ${caseDef.name}[U](u: U) ${hl("extends")} ${enumDef.name}[U] - | } - |""" - } - - class IllegalRedefinitionOfStandardKind(kindType: String, name: Name)(using Context) - extends SyntaxMsg(IllegalRedefinitionOfStandardKindID) { - def msg = e"illegal redefinition of standard $kindType $name" - def explain = - e"""| "$name" is a standard Scala core `$kindType` - | Please choose a different name to avoid conflicts - |""" - } - - class NoExtensionMethodAllowed(mdef: untpd.DefDef)(using Context) - extends SyntaxMsg(NoExtensionMethodAllowedID) { - def msg = e"No extension method allowed here, since collective parameters are given" - def explain = - e"""|Extension method: - | `${mdef}` - |is defined inside an extension clause which has collective parameters. - |""" - } - - class ExtensionMethodCannotHaveTypeParams(mdef: untpd.DefDef)(using Context) - extends SyntaxMsg(ExtensionMethodCannotHaveTypeParamsID) { - def msg = i"Extension method cannot have type parameters since some were already given previously" - - def explain = - e"""|Extension method: - | `${mdef}` - |has type parameters `[${mdef.leadingTypeParams.map(_.show).mkString(",")}]`, while the extension clause has - |it's own type parameters. Please consider moving these to the extension clause's type parameter list. - |""" - } - - class ExtensionCanOnlyHaveDefs(mdef: untpd.Tree)(using Context) - extends SyntaxMsg(ExtensionCanOnlyHaveDefsID) { - def msg = e"Only methods allowed here, since collective parameters are given" - def explain = - e"""Extension clauses can only have `def`s - | `${mdef.show}` is not a valid expression here. - |""" - } - - class UnexpectedPatternForSummonFrom(tree: Tree[_])(using Context) - extends SyntaxMsg(UnexpectedPatternForSummonFromID) { - def msg = e"Unexpected pattern for summonFrom. Expected ${hl("`x: T`")} or ${hl("`_`")}" - def explain = - e"""|The pattern "${tree.show}" provided in the ${hl("case")} expression of the ${hl("summonFrom")}, - | needs to be of the form ${hl("`x: T`")} or ${hl("`_`")}. - | - | Example usage: - | inline def a = summonFrom { - | case x: T => ??? - | } - | - | or - | inline def a = summonFrom { - | case _ => ??? - | } - |""" - } - - class AnonymousInstanceCannotBeEmpty(impl: untpd.Template)(using Context) - extends SyntaxMsg(AnonymousInstanceCannotBeEmptyID) { - def msg = i"anonymous instance must implement a type or have at least one extension method" - def explain = - e"""|Anonymous instances cannot be defined with an empty body. The block - |`${impl.show}` should either contain an implemented type or at least one extension method. - |""" - } - - class ModifierNotAllowedForDefinition(flag: Flag)(using Context) - extends SyntaxMsg(ModifierNotAllowedForDefinitionID) { - def msg = e"Modifier ${hl(flag.flagsString)} is not allowed for this definition" - def explain = "" - } - - class RedundantModifier(flag: Flag)(using Context) - extends SyntaxMsg(RedundantModifierID) { - def msg = e"Modifier ${hl(flag.flagsString)} is redundant for this definition" - def explain = "" - } - - class InvalidReferenceInImplicitNotFoundAnnotation(typeVar: String, owner: String)(using Context) - extends ReferenceMsg(InvalidReferenceInImplicitNotFoundAnnotationID) { - def msg = e"""|Invalid reference to a type variable ${hl(typeVar)} found in the annotation argument. - |The variable does not occur as a parameter in the scope of ${hl(owner)}. - |""" - def explain = "" - } - - class CaseClassInInlinedCode(tree: tpd.Tree)(using Context) - extends SyntaxMsg(CaseClassInInlinedCodeID) { - - def defKind = if tree.symbol.is(Module) then "object" else "class" - def msg = s"Case $defKind definitions are not allowed in inline methods or quoted code. Use a normal $defKind instead." - def explain = - e"""Case class/object definitions generate a considerable footprint in code size. - |Inlining such definition would multiply this footprint for each call site. - |""" - } - - class ImplicitSearchTooLargeWarning(limit: Int, openSearchPairs: List[(Candidate, Type)])(using Context) - extends TypeMsg(ImplicitSearchTooLargeID): - override def showAlways = true - def showQuery(query: (Candidate, Type))(using Context): String = - i" ${query._1.ref.symbol.showLocated} for ${query._2}}" - def msg = - e"""Implicit search problem too large. - |an implicit search was terminated with failure after trying $limit expressions. - |The root candidate for the search was: - | - |${showQuery(openSearchPairs.last)} - | - |You can change the behavior by setting the `-Ximplicit-search-limit` value. - |Smaller values cause the search to fail faster. - |Larger values might make a very large search problem succeed. - |""" - def explain = - e"""The overflow happened with the following lists of tried expressions and target types, - |starting with the root query: - | - |${openSearchPairs.reverse.map(showQuery)}%\n% - """ - - class TargetNameOnTopLevelClass(symbol: Symbol)(using Context) - extends SyntaxMsg(TargetNameOnTopLevelClassID): - def msg = e"${hl("@targetName")} annotation not allowed on top-level $symbol" - def explain = - val annot = symbol.getAnnotation(defn.TargetNameAnnot).get - e"""The @targetName annotation may be applied to a top-level ${hl("val")} or ${hl("def")}, but not - |a top-level ${hl("class")}, ${hl("trait")}, or ${hl("object")}. - | - |This restriction is due to the naming convention of Java classfiles, whose filenames - |are based on the name of the class defined within. If @targetName were permitted - |here, the name of the classfile would be based on the target name, and the compiler - |could not associate that classfile with the Scala-visible defined name of the class. - | - |If your use case requires @targetName, consider wrapping $symbol in an ${hl("object")} - |(and possibly exporting it), as in the following example: - | - |${hl("object Wrapper:")} - | $annot $symbol { ... } - | - |${hl("export")} Wrapper.${symbol.name} ${hl("// optional")}""" - - class NotClassType(tp: Type)(using Context) - extends TypeMsg(NotClassTypeID), ShowMatchTrace(tp): - def msg = ex"$tp is not a class type" - def explain = "" - - class MissingImplicitArgument( - arg: tpd.Tree, - pt: Type, - where: String, - paramSymWithMethodCallTree: Option[(Symbol, tpd.Tree)] = None, - ignoredInstanceNormalImport: => Option[SearchSuccess], - importSuggestionAddendum: Type => String - )(using ctx: Context) extends TypeMsg(MissingImplicitArgumentID): - - def msg: String = - - def formatMsg(shortForm: String)(headline: String = shortForm) = arg match - case arg: Trees.SearchFailureIdent[?] => - arg.tpe match - case _: NoMatchingImplicits => headline - case tpe: SearchFailureType => - i"$headline. ${tpe.explanation}" - case _ => headline - case _ => - arg.tpe match - case tpe: SearchFailureType => - val original = arg match - case Inlined(call, _, _) => call - case _ => arg - i"""$headline. - |I found: - | - | ${original.show.replace("\n", "\n ")} - | - |But ${tpe.explanation}.""" - case _ => headline - - /** Format `raw` implicitNotFound or implicitAmbiguous argument, replacing - * all occurrences of `${X}` where `X` is in `paramNames` with the - * corresponding shown type in `args`. - */ - def userDefinedErrorString(raw: String, paramNames: List[String], args: List[Type]): String = { - def translate(name: String): Option[String] = { - val idx = paramNames.indexOf(name) - if (idx >= 0) Some(ex"${args(idx)}") else None - } - - """\$\{\s*([^}\s]+)\s*\}""".r.replaceAllIn(raw, (_: Regex.Match) match { - case Regex.Groups(v) => quoteReplacement(translate(v).getOrElse("")).nn - }) +class TargetNameOnTopLevelClass(symbol: Symbol)(using Context) +extends SyntaxMsg(TargetNameOnTopLevelClassID): + def msg = e"${hl("@targetName")} annotation not allowed on top-level $symbol" + def explain = + val annot = symbol.getAnnotation(defn.TargetNameAnnot).get + e"""The @targetName annotation may be applied to a top-level ${hl("val")} or ${hl("def")}, but not + |a top-level ${hl("class")}, ${hl("trait")}, or ${hl("object")}. + | + |This restriction is due to the naming convention of Java classfiles, whose filenames + |are based on the name of the class defined within. If @targetName were permitted + |here, the name of the classfile would be based on the target name, and the compiler + |could not associate that classfile with the Scala-visible defined name of the class. + | + |If your use case requires @targetName, consider wrapping $symbol in an ${hl("object")} + |(and possibly exporting it), as in the following example: + | + |${hl("object Wrapper:")} + | $annot $symbol { ... } + | + |${hl("export")} Wrapper.${symbol.name} ${hl("// optional")}""" + +class NotClassType(tp: Type)(using Context) +extends TypeMsg(NotClassTypeID), ShowMatchTrace(tp): + def msg = ex"$tp is not a class type" + def explain = "" + +class MissingImplicitArgument( + arg: tpd.Tree, + pt: Type, + where: String, + paramSymWithMethodCallTree: Option[(Symbol, tpd.Tree)] = None, + ignoredInstanceNormalImport: => Option[SearchSuccess], + importSuggestionAddendum: Type => String + )(using ctx: Context) extends TypeMsg(MissingImplicitArgumentID): + + def msg: String = + + def formatMsg(shortForm: String)(headline: String = shortForm) = arg match + case arg: Trees.SearchFailureIdent[?] => + arg.tpe match + case _: NoMatchingImplicits => headline + case tpe: SearchFailureType => + i"$headline. ${tpe.explanation}" + case _ => headline + case _ => + arg.tpe match + case tpe: SearchFailureType => + val original = arg match + case Inlined(call, _, _) => call + case _ => arg + i"""$headline. + |I found: + | + | ${original.show.replace("\n", "\n ")} + | + |But ${tpe.explanation}.""" + case _ => headline + + /** Format `raw` implicitNotFound or implicitAmbiguous argument, replacing + * all occurrences of `${X}` where `X` is in `paramNames` with the + * corresponding shown type in `args`. + */ + def userDefinedErrorString(raw: String, paramNames: List[String], args: List[Type]): String = { + def translate(name: String): Option[String] = { + val idx = paramNames.indexOf(name) + if (idx >= 0) Some(ex"${args(idx)}") else None } - /** Extract a user defined error message from a symbol `sym` - * with an annotation matching the given class symbol `cls`. - */ - def userDefinedMsg(sym: Symbol, cls: Symbol) = for { - ann <- sym.getAnnotation(cls) - msg <- ann.argumentConstantString(0) - } yield msg - - def location(preposition: String) = if (where.isEmpty) "" else s" $preposition $where" - - def defaultAmbiguousImplicitMsg(ambi: AmbiguousImplicits) = - s"Ambiguous given instances: ${ambi.explanation}${location("of")}" - - def defaultImplicitNotFoundMessage = - ex"No given instance of type $pt was found${location("for")}" - - /** Construct a custom error message given an ambiguous implicit - * candidate `alt` and a user defined message `raw`. - */ - def userDefinedAmbiguousImplicitMsg(alt: SearchSuccess, raw: String) = { - val params = alt.ref.underlying match { - case p: PolyType => p.paramNames.map(_.toString) - case _ => Nil - } - def resolveTypes(targs: List[tpd.Tree])(using Context) = - targs.map(a => Inferencing.fullyDefinedType(a.tpe, "type argument", a.srcPos)) - - // We can extract type arguments from: - // - a function call: - // @implicitAmbiguous("msg A=${A}") - // implicit def f[A](): String = ... - // implicitly[String] // found: f[Any]() - // - // - an eta-expanded function: - // @implicitAmbiguous("msg A=${A}") - // implicit def f[A](x: Int): String = ... - // implicitly[Int => String] // found: x => f[Any](x) - - val call = tpd.closureBody(alt.tree) // the tree itself if not a closure - val targs = tpd.typeArgss(call).flatten - val args = resolveTypes(targs)(using ctx.fresh.setTyperState(alt.tstate)) - userDefinedErrorString(raw, params, args) - } + """\$\{\s*([^}\s]+)\s*\}""".r.replaceAllIn(raw, (_: Regex.Match) match { + case Regex.Groups(v) => quoteReplacement(translate(v).getOrElse("")).nn + }) + } - /** @param rawMsg Message template with variables, e.g. "Variable A is ${A}" - * @param sym Symbol of the annotated type or of the method whose parameter was annotated - * @param substituteType Function substituting specific types for abstract types associated with variables, e.g A -> Int - */ - def formatAnnotationMessage(rawMsg: String, sym: Symbol, substituteType: Type => Type): String = { - val substitutableTypesSymbols = substitutableTypeSymbolsInScope(sym) - - userDefinedErrorString( - rawMsg, - paramNames = substitutableTypesSymbols.map(_.name.unexpandedName.toString), - args = substitutableTypesSymbols.map(_.typeRef).map(substituteType) - ) + /** Extract a user defined error message from a symbol `sym` + * with an annotation matching the given class symbol `cls`. + */ + def userDefinedMsg(sym: Symbol, cls: Symbol) = for { + ann <- sym.getAnnotation(cls) + msg <- ann.argumentConstantString(0) + } yield msg + + def location(preposition: String) = if (where.isEmpty) "" else s" $preposition $where" + + def defaultAmbiguousImplicitMsg(ambi: AmbiguousImplicits) = + s"Ambiguous given instances: ${ambi.explanation}${location("of")}" + + def defaultImplicitNotFoundMessage = + ex"No given instance of type $pt was found${location("for")}" + + /** Construct a custom error message given an ambiguous implicit + * candidate `alt` and a user defined message `raw`. + */ + def userDefinedAmbiguousImplicitMsg(alt: SearchSuccess, raw: String) = { + val params = alt.ref.underlying match { + case p: PolyType => p.paramNames.map(_.toString) + case _ => Nil } + def resolveTypes(targs: List[tpd.Tree])(using Context) = + targs.map(a => Inferencing.fullyDefinedType(a.tpe, "type argument", a.srcPos)) + + // We can extract type arguments from: + // - a function call: + // @implicitAmbiguous("msg A=${A}") + // implicit def f[A](): String = ... + // implicitly[String] // found: f[Any]() + // + // - an eta-expanded function: + // @implicitAmbiguous("msg A=${A}") + // implicit def f[A](x: Int): String = ... + // implicitly[Int => String] // found: x => f[Any](x) + + val call = tpd.closureBody(alt.tree) // the tree itself if not a closure + val targs = tpd.typeArgss(call).flatten + val args = resolveTypes(targs)(using ctx.fresh.setTyperState(alt.tstate)) + userDefinedErrorString(raw, params, args) + } - /** Extracting the message from a method parameter, e.g. in - * - * trait Foo - * - * def foo(implicit @annotation.implicitNotFound("Foo is missing") foo: Foo): Any = ??? - */ - def userDefinedImplicitNotFoundParamMessage: Option[String] = paramSymWithMethodCallTree.flatMap { (sym, applTree) => - userDefinedMsg(sym, defn.ImplicitNotFoundAnnot).map { rawMsg => - val fn = tpd.funPart(applTree) - val targs = tpd.typeArgss(applTree).flatten - val methodOwner = fn.symbol.owner - val methodOwnerType = tpd.qualifier(fn).tpe - val methodTypeParams = fn.symbol.paramSymss.flatten.filter(_.isType) - val methodTypeArgs = targs.map(_.tpe) - val substituteType = (_: Type).asSeenFrom(methodOwnerType, methodOwner).subst(methodTypeParams, methodTypeArgs) - formatAnnotationMessage(rawMsg, sym.owner, substituteType) - } - } + /** @param rawMsg Message template with variables, e.g. "Variable A is ${A}" + * @param sym Symbol of the annotated type or of the method whose parameter was annotated + * @param substituteType Function substituting specific types for abstract types associated with variables, e.g A -> Int + */ + def formatAnnotationMessage(rawMsg: String, sym: Symbol, substituteType: Type => Type): String = { + val substitutableTypesSymbols = substitutableTypeSymbolsInScope(sym) + + userDefinedErrorString( + rawMsg, + paramNames = substitutableTypesSymbols.map(_.name.unexpandedName.toString), + args = substitutableTypesSymbols.map(_.typeRef).map(substituteType) + ) + } - /** Extracting the message from a type, e.g. in - * - * @annotation.implicitNotFound("Foo is missing") - * trait Foo - * - * def foo(implicit foo: Foo): Any = ??? - */ - def userDefinedImplicitNotFoundTypeMessage: Option[String] = - def recur(tp: Type): Option[String] = tp match - case tp: TypeRef => - val sym = tp.symbol - userDefinedImplicitNotFoundTypeMessageFor(sym).orElse(recur(tp.info)) - case tp: ClassInfo => - tp.baseClasses.iterator - .map(userDefinedImplicitNotFoundTypeMessageFor) - .find(_.isDefined).flatten - case tp: TypeProxy => - recur(tp.superType) - case tp: AndType => - recur(tp.tp1).orElse(recur(tp.tp2)) - case _ => - None - recur(pt) - - def userDefinedImplicitNotFoundTypeMessageFor(sym: Symbol): Option[String] = - for - rawMsg <- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot) - if Feature.migrateTo3 || sym != defn.Function1 - // Don't inherit "No implicit view available..." message if subtypes of Function1 are not treated as implicit conversions anymore - yield - val substituteType = (_: Type).asSeenFrom(pt, sym) - formatAnnotationMessage(rawMsg, sym, substituteType) - - def hiddenImplicitsAddendum: String = - def hiddenImplicitNote(s: SearchSuccess) = - i"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." - val normalImports = ignoredInstanceNormalImport.map(hiddenImplicitNote) - normalImports.getOrElse(importSuggestionAddendum(pt)) - - object AmbiguousImplicitMsg { - def unapply(search: SearchSuccess): Option[String] = - userDefinedMsg(search.ref.symbol, defn.ImplicitAmbiguousAnnot) + /** Extracting the message from a method parameter, e.g. in + * + * trait Foo + * + * def foo(implicit @annotation.implicitNotFound("Foo is missing") foo: Foo): Any = ??? + */ + def userDefinedImplicitNotFoundParamMessage: Option[String] = paramSymWithMethodCallTree.flatMap { (sym, applTree) => + userDefinedMsg(sym, defn.ImplicitNotFoundAnnot).map { rawMsg => + val fn = tpd.funPart(applTree) + val targs = tpd.typeArgss(applTree).flatten + val methodOwner = fn.symbol.owner + val methodOwnerType = tpd.qualifier(fn).tpe + val methodTypeParams = fn.symbol.paramSymss.flatten.filter(_.isType) + val methodTypeArgs = targs.map(_.tpe) + val substituteType = (_: Type).asSeenFrom(methodOwnerType, methodOwner).subst(methodTypeParams, methodTypeArgs) + formatAnnotationMessage(rawMsg, sym.owner, substituteType) } + } - arg.tpe match - case ambi: AmbiguousImplicits => - (ambi.alt1, ambi.alt2) match - case (alt @ AmbiguousImplicitMsg(msg), _) => - userDefinedAmbiguousImplicitMsg(alt, msg) - case (_, alt @ AmbiguousImplicitMsg(msg)) => - userDefinedAmbiguousImplicitMsg(alt, msg) - case _ => - defaultAmbiguousImplicitMsg(ambi) - case ambi @ TooUnspecific(target) => - ex"""No implicit search was attempted${location("for")} - |since the expected type $target is not specific enough""" - case _ => - val shortMessage = userDefinedImplicitNotFoundParamMessage - .orElse(userDefinedImplicitNotFoundTypeMessage) - .getOrElse(defaultImplicitNotFoundMessage) - formatMsg(shortMessage)() - ++ hiddenImplicitsAddendum - ++ matchReductionAddendum(pt) - end msg - def explain = "" - end MissingImplicitArgument - - class CannotBeAccessed(tpe: NamedType, superAccess: Boolean)(using Context) - extends ReferenceMsg(CannotBeAccessedID): - def msg = - val pre = tpe.prefix - val name = tpe.name - val alts = tpe.denot.alternatives.map(_.symbol).filter(_.exists) - val whatCanNot = alts match - case Nil => - e"$name cannot" - case sym :: Nil => - e"${if (sym.owner == pre.typeSymbol) sym.show else sym.showLocated} cannot" + /** Extracting the message from a type, e.g. in + * + * @annotation.implicitNotFound("Foo is missing") + * trait Foo + * + * def foo(implicit foo: Foo): Any = ??? + */ + def userDefinedImplicitNotFoundTypeMessage: Option[String] = + def recur(tp: Type): Option[String] = tp match + case tp: TypeRef => + val sym = tp.symbol + userDefinedImplicitNotFoundTypeMessageFor(sym).orElse(recur(tp.info)) + case tp: ClassInfo => + tp.baseClasses.iterator + .map(userDefinedImplicitNotFoundTypeMessageFor) + .find(_.isDefined).flatten + case tp: TypeProxy => + recur(tp.superType) + case tp: AndType => + recur(tp.tp1).orElse(recur(tp.tp2)) case _ => - e"none of the overloaded alternatives named $name can" - val where = if (ctx.owner.exists) s" from ${ctx.owner.enclosingClass}" else "" - val whyNot = new StringBuffer - alts.foreach(_.isAccessibleFrom(pre, superAccess, whyNot)) - e"$whatCanNot be accessed as a member of $pre$where.$whyNot" - def explain = "" + None + recur(pt) + + def userDefinedImplicitNotFoundTypeMessageFor(sym: Symbol): Option[String] = + for + rawMsg <- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot) + if Feature.migrateTo3 || sym != defn.Function1 + // Don't inherit "No implicit view available..." message if subtypes of Function1 are not treated as implicit conversions anymore + yield + val substituteType = (_: Type).asSeenFrom(pt, sym) + formatAnnotationMessage(rawMsg, sym, substituteType) + + def hiddenImplicitsAddendum: String = + def hiddenImplicitNote(s: SearchSuccess) = + i"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." + val normalImports = ignoredInstanceNormalImport.map(hiddenImplicitNote) + normalImports.getOrElse(importSuggestionAddendum(pt)) + + object AmbiguousImplicitMsg { + def unapply(search: SearchSuccess): Option[String] = + userDefinedMsg(search.ref.symbol, defn.ImplicitAmbiguousAnnot) + } + + arg.tpe match + case ambi: AmbiguousImplicits => + (ambi.alt1, ambi.alt2) match + case (alt @ AmbiguousImplicitMsg(msg), _) => + userDefinedAmbiguousImplicitMsg(alt, msg) + case (_, alt @ AmbiguousImplicitMsg(msg)) => + userDefinedAmbiguousImplicitMsg(alt, msg) + case _ => + defaultAmbiguousImplicitMsg(ambi) + case ambi @ TooUnspecific(target) => + ex"""No implicit search was attempted${location("for")} + |since the expected type $target is not specific enough""" + case _ => + val shortMessage = userDefinedImplicitNotFoundParamMessage + .orElse(userDefinedImplicitNotFoundTypeMessage) + .getOrElse(defaultImplicitNotFoundMessage) + formatMsg(shortMessage)() + ++ hiddenImplicitsAddendum + ++ matchReductionAddendum(pt) + end msg + def explain = "" +end MissingImplicitArgument + +class CannotBeAccessed(tpe: NamedType, superAccess: Boolean)(using Context) +extends ReferenceMsg(CannotBeAccessedID): + def msg = + val pre = tpe.prefix + val name = tpe.name + val alts = tpe.denot.alternatives.map(_.symbol).filter(_.exists) + val whatCanNot = alts match + case Nil => + e"$name cannot" + case sym :: Nil => + e"${if (sym.owner == pre.typeSymbol) sym.show else sym.showLocated} cannot" + case _ => + e"none of the overloaded alternatives named $name can" + val where = if (ctx.owner.exists) s" from ${ctx.owner.enclosingClass}" else "" + val whyNot = new StringBuffer + alts.foreach(_.isAccessibleFrom(pre, superAccess, whyNot)) + e"$whatCanNot be accessed as a member of $pre$where.$whyNot" + def explain = "" diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index fe2becfd0907..764d247280d4 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -128,6 +128,23 @@ object ErrorReporting { case _ => anonymousTypeMemberStr(tp) } + /** Explain info of symbol `sym` as a member of class `base`. + * @param showLocation if true also show sym's location. + */ + def infoString(sym: Symbol, base: Type, showLocation: Boolean): String = + val sym1 = sym.underlyingSymbol + def info = base.memberInfo(sym1) + val infoStr = + if sym1.isAliasType then i", which equals ${info.bounds.hi}" + else if sym1.isAbstractOrParamType && info != TypeBounds.empty then i" with bounds$info" + else if sym1.is(Module) then "" + else if sym1.isTerm then i" of type $info" + else "" + i"${if showLocation then sym1.showLocated else sym1}$infoStr" + + def infoStringWithLocation(sym: Symbol, base: Type) = + infoString(sym, base, showLocation = true) + def exprStr(tree: Tree): String = refStr(tree.tpe) def takesNoParamsStr(tree: Tree, kind: String): String = diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index e44a81ed7e32..4fed08127e32 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -319,20 +319,10 @@ object RefChecks { report.error(msg.append(othersMsg), clazz.srcPos) } - def infoString(sym: Symbol) = infoString0(sym, sym.owner != clazz) - def infoStringWithLocation(sym: Symbol) = infoString0(sym, true) - - def infoString0(sym: Symbol, showLocation: Boolean) = { - val sym1 = sym.underlyingSymbol - def info = self.memberInfo(sym1) - val infoStr = - if (sym1.isAliasType) i", which equals ${info.bounds.hi}" - else if (sym1.isAbstractOrParamType && info != TypeBounds.empty) i" with bounds$info" - else if (sym1.is(Module)) "" - else if (sym1.isTerm) i" of type $info" - else "" - i"${if (showLocation) sym1.showLocated else sym1}$infoStr" - } + def infoString(sym: Symbol) = + err.infoString(sym, self, showLocation = sym.owner != clazz) + def infoStringWithLocation(sym: Symbol) = + err.infoString(sym, self, showLocation = true) /* Check that all conditions for overriding `other` by `member` * of class `clazz` are met. @@ -347,20 +337,9 @@ object RefChecks { def noErrorType = !memberTp(self).isErroneous && !otherTp(self).isErroneous - def overrideErrorMsg(msg: String, compareTypes: Boolean = false): Message = { - val isConcreteOverAbstract = - (other.owner isSubClass member.owner) && other.is(Deferred) && !member.is(Deferred) - val addendum = - if isConcreteOverAbstract then - ";\n (Note that %s is abstract,\n and is therefore overridden by concrete %s)".format( - infoStringWithLocation(other), - infoStringWithLocation(member)) - else "" - val fullMsg = - s"error overriding ${infoStringWithLocation(other)};\n ${infoString(member)} $msg$addendum" - if compareTypes then OverrideTypeMismatchError(fullMsg, memberTp(self), otherTp(self)) - else OverrideError(fullMsg) - } + def overrideErrorMsg(core: Context ?=> String, compareTypes: Boolean = false): Message = + val (mtp, otp) = if compareTypes then (memberTp(self), otherTp(self)) else (NoType, NoType) + OverrideError(core, self, member, other, mtp, otp) def compatTypes(memberTp: Type, otherTp: Type): Boolean = try diff --git a/compiler/test-resources/repl/i1370 b/compiler/test-resources/repl/i1370 index 6582e03b6539..4bd92b4d5f83 100644 --- a/compiler/test-resources/repl/i1370 +++ b/compiler/test-resources/repl/i1370 @@ -1,5 +1,5 @@ scala> object Lives { class Private { def foo1: Any = new Private.C1; def foo2: Any = new Private.C2 }; object Private { class C1 private {}; private class C2 {} } } --- Error: ---------------------------------------------------------------------- +-- [E173] Reference Error: ----------------------------------------------------- 1 | object Lives { class Private { def foo1: Any = new Private.C1; def foo2: Any = new Private.C2 }; object Private { class C1 private {}; private class C2 {} } } | ^^^^^^^^^^ |constructor C1 cannot be accessed as a member of Lives.Private.C1 from class Private. diff --git a/tests/neg-custom-args/captures/lazylist.check b/tests/neg-custom-args/captures/lazylist.check index ec620b39da08..c594519496cd 100644 --- a/tests/neg-custom-args/captures/lazylist.check +++ b/tests/neg-custom-args/captures/lazylist.check @@ -1,4 +1,4 @@ --- [E163] Declaration Error: tests/neg-custom-args/captures/lazylist.scala:22:6 ---------------------------------------- +-- [E164] Declaration Error: tests/neg-custom-args/captures/lazylist.scala:22:6 ---------------------------------------- 22 | def tail: {*} LazyList[Nothing] = ??? // error overriding | ^ | error overriding method tail in class LazyList of type -> lazylists.LazyList[Nothing]; diff --git a/tests/neg-custom-args/captures/lazylists2.check b/tests/neg-custom-args/captures/lazylists2.check index 41881b57da24..d26d373443e3 100644 --- a/tests/neg-custom-args/captures/lazylists2.check +++ b/tests/neg-custom-args/captures/lazylists2.check @@ -1,4 +1,4 @@ --- [E163] Declaration Error: tests/neg-custom-args/captures/lazylists2.scala:50:10 ------------------------------------- +-- [E164] Declaration Error: tests/neg-custom-args/captures/lazylists2.scala:50:10 ------------------------------------- 50 | def tail: {xs, f} LazyList[B] = xs.tail.map(f) // error | ^ | error overriding method tail in trait LazyList of type -> {Mapped.this} LazyList[B]; diff --git a/tests/neg/abstract-givens.check b/tests/neg/abstract-givens.check index a74d0097b091..022c454c31f1 100644 --- a/tests/neg/abstract-givens.check +++ b/tests/neg/abstract-givens.check @@ -7,7 +7,7 @@ | ^ | error overriding given instance y in trait T of type (using x$1: Int): String; | given instance y of type (using x$1: Int): String cannot override final member given instance y in trait T --- [E163] Declaration Error: tests/neg/abstract-givens.scala:9:8 ------------------------------------------------------- +-- [E164] Declaration Error: tests/neg/abstract-givens.scala:9:8 ------------------------------------------------------- 9 | given z[T](using T): Seq[T] = List(summon[T]) // error | ^ | error overriding given instance z in trait T of type [T](using x$1: T): List[T]; From cdabf411b2e24639832ac98fdca958d171181d94 Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 23 Nov 2022 20:19:41 +0100 Subject: [PATCH 06/14] Move rendering logic from interpolators to Message --- .../dotty/tools/dotc/cc/CheckCaptures.scala | 3 +- .../dotty/tools/dotc/core/Decorators.scala | 4 +- .../src/dotty/tools/dotc/core/Types.scala | 5 + .../tools/dotc/printing/Formatting.scala | 245 +----- .../tools/dotc/printing/PlainPrinter.scala | 2 +- .../dotty/tools/dotc/reporting/Message.scala | 303 ++++++-- .../dotty/tools/dotc/reporting/WConf.scala | 2 +- .../dotty/tools/dotc/reporting/messages.scala | 720 +++++++++--------- .../dotc/transform/PCPCheckAndHeal.scala | 5 +- .../src/dotty/tools/dotc/typer/Checking.scala | 15 +- .../dotty/tools/dotc/typer/Implicits.scala | 14 +- .../src/dotty/tools/dotc/typer/Namer.scala | 2 +- compiler/test-resources/repl/i4184 | 7 +- .../tools/dotc/StringFormatterTest.scala | 45 -- .../dotty/tools/vulpix/ParallelTesting.scala | 2 +- .../dotty/tools/xsbt/DelegatingReporter.java | 2 +- .../test/dotty/tools/scaladoc/testUtils.scala | 6 +- tests/neg-custom-args/i13838.check | 10 +- tests/neg/exports.check | 2 +- tests/neg/i15507.check | 40 + tests/neg/i15507.scala | 4 +- tests/neg/i827.check | 11 + tests/neg/i8623.check | 2 + tests/neg/i9568.check | 16 +- 24 files changed, 727 insertions(+), 740 deletions(-) create mode 100644 tests/neg/i15507.check create mode 100644 tests/neg/i827.check diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 899914e872c8..3532e11ea3ef 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -921,7 +921,8 @@ class CheckCaptures extends Recheck, SymTransformer: em"""Non-local $sym cannot have an inferred$resultStr type |$inferred |with non-empty capture set $refs. - |The type needs to be declared explicitly.""", t.srcPos) + |The type needs to be declared explicitly.""".withoutDisambiguation(), + t.srcPos) case _ => inferred.foreachPart(checkPure, StopAt.Static) case t @ TypeApply(fun, args) => diff --git a/compiler/src/dotty/tools/dotc/core/Decorators.scala b/compiler/src/dotty/tools/dotc/core/Decorators.scala index cc4319349008..051b3295c3f2 100644 --- a/compiler/src/dotty/tools/dotc/core/Decorators.scala +++ b/compiler/src/dotty/tools/dotc/core/Decorators.scala @@ -319,7 +319,7 @@ object Decorators { * their arguments are "non-sensical". */ def e(args: Shown*)(using Context): String = - forErrorMessages(new StringFormatter(sc).assemble(args)) + new StringFormatter(sc).assemble(args) /** A NoExplanation message formatted with `e` */ def em(args: Shown*)(using Context): NoExplanation = @@ -332,7 +332,7 @@ object Decorators { * give more info about type variables and to disambiguate where needed. */ def ex(args: Shown*)(using Context): String = - explained(new StringFormatter(sc).assemble(args)) + new StringFormatter(sc).assemble(args) extension [T <: AnyRef](arr: Array[T]) def binarySearch(x: T | Null): Int = java.util.Arrays.binarySearch(arr.asInstanceOf[Array[Object | Null]], x) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 98a0c540323d..f7d68623e609 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -5291,7 +5291,12 @@ object Types { abstract class FlexType extends UncachedGroundType with ValueType abstract class ErrorType extends FlexType { + + /** An explanation of the cause of the failure */ def msg(using Context): Message + + /** An explanation of the cause of the failure as a string */ + def explanation(using Context): String = msg.message } object ErrorType: diff --git a/compiler/src/dotty/tools/dotc/printing/Formatting.scala b/compiler/src/dotty/tools/dotc/printing/Formatting.scala index f85845517d8c..f4bbd74842c8 100644 --- a/compiler/src/dotty/tools/dotc/printing/Formatting.scala +++ b/compiler/src/dotty/tools/dotc/printing/Formatting.scala @@ -137,236 +137,6 @@ object Formatting { } } - /** The `em` string interpolator works like the `i` string interpolator, but marks nonsensical errors - * using `...` tags. - * Note: Instead of these tags, it would be nicer to return a data structure containing the message string - * and a boolean indicating whether the message is sensical, but then we cannot use string operations - * like concatenation, stripMargin etc on the values returned by em"...", and in the current error - * message composition methods, this is crucial. - */ - def forErrorMessages(op: Context ?=> String)(using Context): String = op(using errorMessageCtx) - - private class ErrorMessagePrinter(_ctx: Context) extends RefinedPrinter(_ctx): - override def toText(tp: Type): Text = wrapNonSensical(tp, super.toText(tp)) - override def toText(sym: Symbol): Text = wrapNonSensical(sym, super.toText(sym)) - - private def wrapNonSensical(arg: Any, text: Text)(using Context): Text = { - import Message._ - def isSensical(arg: Any): Boolean = arg match { - case tpe: Type => - tpe.exists && !tpe.isErroneous - case sym: Symbol if sym.isCompleted => - sym.info match { - case _: ErrorType | TypeAlias(_: ErrorType) | NoType => false - case _ => true - } - case _ => true - } - - if (isSensical(arg)) text - else nonSensicalStartTag ~ text ~ nonSensicalEndTag - } - - private type Recorded = Symbol | ParamRef | SkolemType - - private case class SeenKey(str: String, isType: Boolean) - private class Seen extends mutable.HashMap[SeenKey, List[Recorded]] { - - override def default(key: SeenKey) = Nil - - def record(str: String, isType: Boolean, entry: Recorded)(using Context): String = { - - /** If `e1` is an alias of another class of the same name, return the other - * class symbol instead. This normalization avoids recording e.g. scala.List - * and scala.collection.immutable.List as two different types - */ - def followAlias(e1: Recorded): Recorded = e1 match { - case e1: Symbol if e1.isAliasType => - val underlying = e1.typeRef.underlyingClassRef(refinementOK = false).typeSymbol - if (underlying.name == e1.name) underlying else e1 - case _ => e1 - } - val key = SeenKey(str, isType) - val existing = apply(key) - lazy val dealiased = followAlias(entry) - - // alts: The alternatives in `existing` that are equal, or follow (an alias of) `entry` - var alts = existing.dropWhile(alt => dealiased ne followAlias(alt)) - if (alts.isEmpty) { - alts = entry :: existing - update(key, alts) - } - val suffix = alts.length match { - case 1 => "" - case n => n.toString.toCharArray.map { - case '0' => '⁰' - case '1' => '¹' - case '2' => '²' - case '3' => '³' - case '4' => '⁴' - case '5' => '⁵' - case '6' => '⁶' - case '7' => '⁷' - case '8' => '⁸' - case '9' => '⁹' - }.mkString - } - str + suffix - } - } - - private class ExplainingPrinter(seen: Seen)(_ctx: Context) extends ErrorMessagePrinter(_ctx) { - - /** True if printer should a source module instead of its module class */ - private def useSourceModule(sym: Symbol): Boolean = - sym.is(ModuleClass, butNot = Package) && sym.sourceModule.exists && !_ctx.settings.YdebugNames.value - - override def simpleNameString(sym: Symbol): String = - if (useSourceModule(sym)) simpleNameString(sym.sourceModule) - else seen.record(super.simpleNameString(sym), sym.isType, sym) - - override def ParamRefNameString(param: ParamRef): String = - seen.record(super.ParamRefNameString(param), param.isInstanceOf[TypeParamRef], param) - - override def toTextRef(tp: SingletonType): Text = tp match { - case tp: SkolemType => seen.record(tp.repr.toString, isType = true, tp) - case _ => super.toTextRef(tp) - } - - override def toText(tp: Type): Text = tp match { - case tp: TypeRef if useSourceModule(tp.symbol) => Str("object ") ~ super.toText(tp) - case _ => super.toText(tp) - } - } - - /** Create explanation for single `Recorded` type or symbol */ - def explanation(entry: AnyRef)(using Context): String = { - def boundStr(bound: Type, default: ClassSymbol, cmp: String) = - if (bound.isRef(default)) "" else i"$cmp $bound" - - def boundsStr(bounds: TypeBounds): String = { - val lo = boundStr(bounds.lo, defn.NothingClass, ">:") - val hi = boundStr(bounds.hi, defn.AnyClass, "<:") - if (lo.isEmpty) hi - else if (hi.isEmpty) lo - else s"$lo and $hi" - } - - def addendum(cat: String, info: Type): String = info match { - case bounds @ TypeBounds(lo, hi) if bounds ne TypeBounds.empty => - if (lo eq hi) i" which is an alias of $lo" - else i" with $cat ${boundsStr(bounds)}" - case _ => - "" - } - - entry match { - case param: TypeParamRef => - s"is a type variable${addendum("constraint", TypeComparer.bounds(param))}" - case param: TermParamRef => - s"is a reference to a value parameter" - case sym: Symbol => - val info = - if (ctx.gadt.contains(sym)) - sym.info & ctx.gadt.fullBounds(sym) - else - sym.info - s"is a ${ctx.printer.kindString(sym)}${sym.showExtendedLocation}${addendum("bounds", info)}" - case tp: SkolemType => - s"is an unknown value of type ${tp.widen.show}" - } - } - - /** Turns a `Seen` into a `String` to produce an explanation for types on the - * form `where: T is...` - * - * @return string disambiguating types - */ - private def explanations(seen: Seen)(using Context): String = { - def needsExplanation(entry: Recorded) = entry match { - case param: TypeParamRef => ctx.typerState.constraint.contains(param) - case param: ParamRef => false - case skolem: SkolemType => true - case sym: Symbol => - ctx.gadt.contains(sym) && ctx.gadt.fullBounds(sym) != TypeBounds.empty - } - - val toExplain: List[(String, Recorded)] = seen.toList.flatMap { kvs => - val res: List[(String, Recorded)] = kvs match { - case (key, entry :: Nil) => - if (needsExplanation(entry)) (key.str, entry) :: Nil else Nil - case (key, entries) => - for (alt <- entries) yield { - val tickedString = seen.record(key.str, key.isType, alt) - (tickedString, alt) - } - } - res // help the inferrencer out - }.sortBy(_._1) - - def columnar(parts: List[(String, String)]): List[String] = { - lazy val maxLen = parts.map(_._1.length).max - parts.map { - case (leader, trailer) => - val variable = hl(leader) - s"""$variable${" " * (maxLen - leader.length)} $trailer""" - } - } - - val explainParts = toExplain.map { case (str, entry) => (str, explanation(entry)) } - val explainLines = columnar(explainParts) - if (explainLines.isEmpty) "" else i"where: $explainLines%\n %\n" - } - - private def errorMessageCtx(using Context): Context = - val ctx1 = ctx.property(MessageLimiter) match - case Some(_: ErrorMessageLimiter) => ctx - case _ => ctx.fresh.setProperty(MessageLimiter, ErrorMessageLimiter()) - ctx1.printer match - case _: ErrorMessagePrinter => ctx1 - case _ => ctx1.fresh.setPrinterFn(ctx => ErrorMessagePrinter(ctx)) - - /** Context with correct printer set for explanations */ - private def explainCtx(seen: Seen)(using Context): Context = - val ectx = errorMessageCtx - ectx.printer match - case dp: ExplainingPrinter => - ectx // re-use outer printer and defer explanation to it - case _ => - ectx.fresh.setPrinterFn(ctx => new ExplainingPrinter(seen)(ctx)) - - /** Entrypoint for explanation string interpolator: - * - * ``` - * ex"disambiguate $tpe1 and $tpe2" - * ``` - */ - def explained(op: Context ?=> String)(using Context): String = { - val seen = new Seen - val msg = op(using explainCtx(seen)) - val addendum = explanations(seen) - if (addendum.isEmpty) msg else msg ++ "\n\n" ++ addendum - } - - /** When getting a type mismatch it is useful to disambiguate placeholders like: - * - * ``` - * found: List[Int] - * required: List[T] - * where: T is a type in the initializer of value s which is an alias of - * String - * ``` - * - * @return the `where` section as well as the printing context for the - * placeholders - `("T is a...", printCtx)` - */ - def disambiguateTypes(args: Type*)(using Context): (String, Context) = { - val seen = new Seen - val printCtx = explainCtx(seen) - args.foreach(_.show(using printCtx)) // showing each member will put it into `seen` - (explanations(seen), printCtx) - } - /** This method will produce a colored type diff from the given arguments. * The idea is to do this for known cases that are useful and then fall back * on regular syntax highlighting for the cases which are unhandled. @@ -378,16 +148,13 @@ object Formatting { * @return the (found, expected, changePercentage) with coloring to * highlight the difference */ - def typeDiff(found: Type, expected: Type)(using Context): (String, String) = { - val fnd = wrapNonSensical(found, found.toText(ctx.printer)).show - val exp = wrapNonSensical(expected, expected.toText(ctx.printer)).show - - DiffUtil.mkColoredTypeDiff(fnd, exp) match { - case _ if ctx.settings.color.value == "never" => (fnd, exp) - case (fnd, exp, change) if change < 0.5 => (fnd, exp) + def typeDiff(found: Type, expected: Type)(using Context): (String, String) = + val fnd = found.show + val exp = expected.show + DiffUtil.mkColoredTypeDiff(fnd, exp) match + case (fnd1, exp1, change) + if change < 0.5 && ctx.settings.color.value != "never" => (fnd1, exp1) case _ => (fnd, exp) - } - } /** Explicit syntax highlighting */ def hl(s: String)(using Context): String = diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index f0479f818c9f..0da1993310c6 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -224,7 +224,7 @@ class PlainPrinter(_ctx: Context) extends Printer { case tp: PreviousErrorType if ctx.settings.XprintTypes.value => "" // do not print previously reported error message because they may try to print this error type again recuresevely case tp: ErrorType => - s"" + s"" case tp: WildcardType => if (tp.optBounds.exists) "" else "" case NoType => diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index 205a60a236f5..e5a654027daf 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -2,17 +2,18 @@ package dotty.tools package dotc package reporting -import core.Contexts.*, core.Decorators.*, core.Mode +import core.* +import Contexts.*, Decorators.*, Symbols.*, Types.*, Flags.* +import printing.{RefinedPrinter, MessageLimiter, ErrorMessageLimiter} +import printing.Texts.Text +import printing.Formatting.hl import config.SourceVersion import scala.language.unsafeNulls import scala.annotation.threadUnsafe -object Message { - val nonSensicalStartTag: String = "" - val nonSensicalEndTag: String = "" - +object Message: def rewriteNotice(what: String, version: SourceVersion | Null = null, options: String = "")(using Context): String = if !ctx.mode.is(Mode.Interactive) then val sourceStr = if version != null then i"-source $version" else "" @@ -22,7 +23,173 @@ object Message { else i"$sourceStr $options" i"\n$what can be rewritten automatically under -rewrite $optionStr." else "" -} + + private type Recorded = Symbol | ParamRef | SkolemType + + private case class SeenKey(str: String, isType: Boolean) + + private class Seen(disambiguate: Boolean) extends collection.mutable.HashMap[SeenKey, List[Recorded]]: + override def default(key: SeenKey) = Nil + + var nonSensical = false + private var recordOK = disambiguate + + def stopRecording() = + recordOK = false + + def record(str: String, isType: Boolean, entry: Recorded)(using Context): String = { + if !recordOK then return str + //println(s"recording $str, $isType, $entry") + + /** If `e1` is an alias of another class of the same name, return the other + * class symbol instead. This normalization avoids recording e.g. scala.List + * and scala.collection.immutable.List as two different types + */ + def followAlias(e1: Recorded): Recorded = e1 match { + case e1: Symbol if e1.isAliasType => + val underlying = e1.typeRef.underlyingClassRef(refinementOK = false).typeSymbol + if (underlying.name == e1.name) underlying else e1 + case _ => e1 + } + val key = SeenKey(str, isType) + val existing = apply(key) + lazy val dealiased = followAlias(entry) + + // alts: The alternatives in `existing` that are equal, or follow (an alias of) `entry` + var alts = existing.dropWhile(alt => dealiased ne followAlias(alt)) + if (alts.isEmpty) { + alts = entry :: existing + update(key, alts) + } + val suffix = alts.length match { + case 1 => "" + case n => n.toString.toCharArray.map { + case '0' => '⁰' + case '1' => '¹' + case '2' => '²' + case '3' => '³' + case '4' => '⁴' + case '5' => '⁵' + case '6' => '⁶' + case '7' => '⁷' + case '8' => '⁸' + case '9' => '⁹' + }.mkString + } + str + suffix + } + end Seen + + /** Create explanation for single `Recorded` type or symbol */ + def explanation(entry: AnyRef)(using Context): String = + def boundStr(bound: Type, default: ClassSymbol, cmp: String) = + if (bound.isRef(default)) "" else i"$cmp $bound" + + def boundsStr(bounds: TypeBounds): String = { + val lo = boundStr(bounds.lo, defn.NothingClass, ">:") + val hi = boundStr(bounds.hi, defn.AnyClass, "<:") + if (lo.isEmpty) hi + else if (hi.isEmpty) lo + else s"$lo and $hi" + } + + def addendum(cat: String, info: Type): String = info match { + case bounds @ TypeBounds(lo, hi) if bounds ne TypeBounds.empty => + if (lo eq hi) i" which is an alias of $lo" + else i" with $cat ${boundsStr(bounds)}" + case _ => + "" + } + + entry match { + case param: TypeParamRef => + s"is a type variable${addendum("constraint", TypeComparer.bounds(param))}" + case param: TermParamRef => + s"is a reference to a value parameter" + case sym: Symbol => + val info = + if (ctx.gadt.contains(sym)) + sym.info & ctx.gadt.fullBounds(sym) + else + sym.info + s"is a ${ctx.printer.kindString(sym)}${sym.showExtendedLocation}${addendum("bounds", info)}" + case tp: SkolemType => + s"is an unknown value of type ${tp.widen.show}" + } + end explanation + + /** Turns a `Seen` into a `String` to produce an explanation for types on the + * form `where: T is...` + * + * @return string disambiguating types + */ + private def explanations(seen: Seen)(using Context): String = + def needsExplanation(entry: Recorded) = entry match { + case param: TypeParamRef => ctx.typerState.constraint.contains(param) + case param: ParamRef => false + case skolem: SkolemType => true + case sym: Symbol => + ctx.gadt.contains(sym) && ctx.gadt.fullBounds(sym) != TypeBounds.empty + } + + val toExplain: List[(String, Recorded)] = seen.toList.flatMap { kvs => + val res: List[(String, Recorded)] = kvs match { + case (key, entry :: Nil) => + if (needsExplanation(entry)) (key.str, entry) :: Nil else Nil + case (key, entries) => + for (alt <- entries) yield { + val tickedString = seen.record(key.str, key.isType, alt) + (tickedString, alt) + } + } + res // help the inferrencer out + }.sortBy(_._1) + + def columnar(parts: List[(String, String)]): List[String] = { + lazy val maxLen = parts.map(_._1.length).max + parts.map { + case (leader, trailer) => + val variable = hl(leader) + s"""$variable${" " * (maxLen - leader.length)} $trailer""" + } + } + + val explainParts = toExplain.map { case (str, entry) => (str, explanation(entry)) } + val explainLines = columnar(explainParts) + if (explainLines.isEmpty) "" else i"where: $explainLines%\n %\n" + end explanations + + private class Printer(val seen: Seen, _ctx: Context) extends RefinedPrinter(_ctx): + + /** True if printer should a show source module instead of its module class */ + private def useSourceModule(sym: Symbol): Boolean = + sym.is(ModuleClass, butNot = Package) && sym.sourceModule.exists && !_ctx.settings.YdebugNames.value + + override def simpleNameString(sym: Symbol): String = + if useSourceModule(sym) then simpleNameString(sym.sourceModule) + else seen.record(super.simpleNameString(sym), sym.isType, sym) + + override def ParamRefNameString(param: ParamRef): String = + seen.record(super.ParamRefNameString(param), param.isInstanceOf[TypeParamRef], param) + + override def toTextRef(tp: SingletonType): Text = tp match + case tp: SkolemType => seen.record(tp.repr.toString, isType = true, tp) + case _ => super.toTextRef(tp) + + override def toText(tp: Type): Text = + if !tp.exists || tp.isErroneous then seen.nonSensical = true + tp match + case tp: TypeRef if useSourceModule(tp.symbol) => Str("object ") ~ super.toText(tp) + case _ => super.toText(tp) + + override def toText(sym: Symbol): Text = + sym.infoOrCompleter match + case _: ErrorType | TypeAlias(_: ErrorType) | NoType => seen.nonSensical = true + case _ => + super.toText(sym) + end Printer + +end Message /** A `Message` contains all semantic information necessary to easily * comprehend what caused the message to be logged. Each message can be turned @@ -41,7 +208,13 @@ object Message { * used to reference documentation online */ abstract class Message(val errorId: ErrorMessageID)(using Context) { self => - import Message._ + import Message.* + + /** The kind of the error message, e.g. "Syntax" or "Type Mismatch". + * This will be printed as "$kind Error", "$kind Warning", etc, on the first + * line of the message. + */ + def kind: MessageKind /** The `msg` contains the diagnostic message e.g: * @@ -52,22 +225,27 @@ abstract class Message(val errorId: ErrorMessageID)(using Context) { self => * `Diagnostic`. The message is given in raw form, with possible embedded * tags. */ - protected def msg: String - - /** The kind of the error message, e.g. "Syntax" or "Type Mismatch". - * This will be printed as "$kind Error", "$kind Warning", etc, on the first - * line of the message. - */ - def kind: MessageKind + protected def msg(using Context): String /** The explanation should provide a detailed description of why the error * occurred and use examples from the user's own code to illustrate how to * avoid these errors. It might contain embedded tags. */ - protected def explain: String + protected def explain(using Context): String - /** A message suffix that can be added for certain subclasses */ - protected def msgSuffix: String = "" + /** What gets printed after the message proper */ + protected def msgPostscript(using Context): String = + if ctx eq NoContext then "" + else ctx.printer match + case msgPrinter: Message.Printer => + myIsNonSensical = msgPrinter.seen.nonSensical + val addendum = explanations(msgPrinter.seen) + msgPrinter.seen.clear() + msgPrinter.seen.stopRecording() + if addendum.isEmpty || !disambiguate then "" + else "\n\n" ++ addendum + case _ => + "" /** Does this message have an explanation? * This is normally the same as `explain.nonEmpty` but can be overridden @@ -76,66 +254,71 @@ abstract class Message(val errorId: ErrorMessageID)(using Context) { self => */ def canExplain: Boolean = explain.nonEmpty - private var myMsg: String | Null = null - private var myIsNonSensical: Boolean = false + var myIsNonSensical: Boolean = false + + /** A message is non-sensical if it contains references to internally + * generated error types. Normally we want to suppress error messages + * referring to types like this because they look weird and are normally + * follow-up errors to something that was diagnosed before. + */ + def isNonSensical: Boolean = { message; myIsNonSensical } + + private var disambiguate: Boolean = true - private def dropNonSensical(msg: String): String = - if msg.contains(nonSensicalStartTag) then - myIsNonSensical = true - // myMsg might be composed of several d"..." invocations -> nested - // nonsensical tags possible - msg - .replace(nonSensicalStartTag, "") - .replace(nonSensicalEndTag, "") - else msg + def withoutDisambiguation(): this.type = + disambiguate = false + this - /** The message with potential embedded tags */ - def rawMessage = message + private def inMessageContext(disambiguate: Boolean)(op: Context ?=> String): String = + if ctx eq NoContext then op + else + val (msgContext, msgPrinter: Message.Printer) = ctx.printer match + case printer: Message.Printer => (ctx, printer) + case _ => + val seen = Seen(disambiguate) + val ctx1 = ctx.fresh.setPrinterFn(Message.Printer(seen, _)) + if !ctx1.property(MessageLimiter).isDefined then + ctx1.setProperty(MessageLimiter, ErrorMessageLimiter()) + (ctx1, ctx1.printer.asInstanceOf[Message.Printer]) + op(using msgContext) /** The message to report. tags are filtered out */ - @threadUnsafe lazy val message: String = dropNonSensical(msg + msgSuffix) + @threadUnsafe lazy val message: String = + inMessageContext(disambiguate)(msg + msgPostscript) /** The explanation to report. tags are filtered out */ - @threadUnsafe lazy val explanation: String = dropNonSensical(explain) - - /** A message is non-sensical if it contains references to - * tags. Such tags are inserted by the error diagnostic framework if a - * message contains references to internally generated error types. Normally - * we want to suppress error messages referring to types like this because - * they look weird and are normally follow-up errors to something that was - * diagnosed before. - */ - def isNonSensical: Boolean = { message; myIsNonSensical } + @threadUnsafe lazy val explanation: String = + inMessageContext(disambiguate = false)(explain) /** The implicit `Context` in messages is a large thing that we don't want * persisted. This method gets around that by duplicating the message, * forcing its `msg` and `explanation` vals and dropping the implicit context * that was captured in the original message. */ - def persist: Message = new Message(errorId) { - val kind = self.kind - val msg = self.msg - val explain = self.explain + def persist: Message = new Message(errorId)(using NoContext): + val kind = self.kind + private val persistedMsg = self.message + private val persistedExplain = self.explanation + def msg(using Context) = persistedMsg + def explain(using Context) = persistedExplain override val canExplain = self.canExplain - } + override def isNonSensical = self.isNonSensical def append(suffix: => String): Message = mapMsg(_ ++ suffix) + def prepend(prefix: => String): Message = mapMsg(prefix ++ _) def mapMsg(f: String => String): Message = new Message(errorId): - val kind = self.kind - def msg = f(self.msg) - def explain = self.explain + val kind = self.kind + def msg(using Context) = f(self.msg) + def explain(using Context) = self.explain override def canExplain = self.canExplain def appendExplanation(suffix: => String): Message = new Message(errorId): - val kind = self.kind - def msg = self.msg - def explain = self.explain ++ suffix + val kind = self.kind + def msg(using Context) = self.msg + def explain(using Context) = self.explain ++ suffix override def canExplain = true - def wrap(header: => String, trailer: => String): Message = - mapMsg(s => header ++ s ++ trailer) - /** Override with `true` for messages that should always be shown even if their * position overlaps another messsage of a different class. On the other hand * multiple messages of the same class with overlapping positions will lead @@ -146,10 +329,14 @@ abstract class Message(val errorId: ErrorMessageID)(using Context) { self => override def toString = msg } +/** A marker trait that suppresses generation of `where` clause for disambiguations */ +trait NoDisambiguation extends Message: + withoutDisambiguation() + /** The fallback `Message` containing no explanation and having no `kind` */ -final class NoExplanation(msgFn: => String)(using Context) extends Message(ErrorMessageID.NoExplanationID) { - def msg: String = msgFn - def explain: String = "" +final class NoExplanation(msgFn: Context ?=> String)(using Context) extends Message(ErrorMessageID.NoExplanationID) { + def msg(using Context): String = msgFn + def explain(using Context): String = "" val kind: MessageKind = MessageKind.NoKind override def toString(): String = msg diff --git a/compiler/src/dotty/tools/dotc/reporting/WConf.scala b/compiler/src/dotty/tools/dotc/reporting/WConf.scala index 21e10e894e0b..af1a5c0f0f47 100644 --- a/compiler/src/dotty/tools/dotc/reporting/WConf.scala +++ b/compiler/src/dotty/tools/dotc/reporting/WConf.scala @@ -18,7 +18,7 @@ enum MessageFilter: case Feature => message.isInstanceOf[Diagnostic.FeatureWarning] case Unchecked => message.isInstanceOf[Diagnostic.UncheckedWarning] case MessagePattern(pattern) => - val noHighlight = message.msg.rawMessage.replaceAll("\\e\\[[\\d;]*[^\\d;]","") + val noHighlight = message.msg.message.replaceAll("\\e\\[[\\d;]*[^\\d;]","") pattern.findFirstIn(noHighlight).nonEmpty case MessageID(errorId) => message.msg.errorId == errorId case None => false diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 63b9fc160443..477192c2402e 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -50,15 +50,16 @@ abstract class TypeMsg(errorId: ErrorMessageID)(using Context) extends Message(e def kind = MessageKind.Type trait ShowMatchTrace(tps: Type*)(using Context) extends Message: - override def msgSuffix: String = matchReductionAddendum(tps*) + override def msgPostscript(using Context): String = + super.msgPostscript ++ matchReductionAddendum(tps*) abstract class TypeMismatchMsg(found: Type, expected: Type)(errorId: ErrorMessageID)(using Context) extends Message(errorId), ShowMatchTrace(found, expected): def kind = MessageKind.TypeMismatch - def explain = err.whyNoMatchStr(found, expected) + def explain(using Context) = err.whyNoMatchStr(found, expected) override def canExplain = true -abstract class NamingMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): +abstract class NamingMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId), NoDisambiguation: def kind = MessageKind.Naming abstract class DeclarationMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): @@ -83,7 +84,7 @@ abstract class ReferenceMsg(errorId: ErrorMessageID)(using Context) extends Mess abstract class EmptyCatchOrFinallyBlock(tryBody: untpd.Tree, errNo: ErrorMessageID)(using Context) extends SyntaxMsg(errNo) { - def explain = { + def explain(using Context) = { val tryString = tryBody match { case Block(Nil, untpd.EmptyTree) => "{}" case _ => tryBody.show @@ -119,23 +120,23 @@ extends SyntaxMsg(errNo) { class EmptyCatchBlock(tryBody: untpd.Tree)(using Context) extends EmptyCatchOrFinallyBlock(tryBody, EmptyCatchBlockID) { - def msg = + def msg(using Context) = e"""|The ${hl("catch")} block does not contain a valid expression, try |adding a case like - ${hl("case e: Exception =>")} to the block""" } class EmptyCatchAndFinallyBlock(tryBody: untpd.Tree)(using Context) extends EmptyCatchOrFinallyBlock(tryBody, EmptyCatchAndFinallyBlockID) { - def msg = + def msg(using Context) = e"""|A ${hl("try")} without ${hl("catch")} or ${hl("finally")} is equivalent to putting |its body in a block; no exceptions are handled.""" } class DeprecatedWithOperator()(using Context) extends SyntaxMsg(DeprecatedWithOperatorID) { - def msg = + def msg(using Context) = e"""${hl("with")} as a type operator has been deprecated; use ${hl("&")} instead""" - def explain = + def explain(using Context) = e"""|Dotty introduces intersection types - ${hl("&")} types. These replace the |use of the ${hl("with")} keyword. There are a few differences in |semantics between intersection types and using ${hl("with")}.""" @@ -143,10 +144,10 @@ extends SyntaxMsg(DeprecatedWithOperatorID) { class CaseClassMissingParamList(cdef: untpd.TypeDef)(using Context) extends SyntaxMsg(CaseClassMissingParamListID) { - def msg = + def msg(using Context) = e"""|A ${hl("case class")} must have at least one parameter list""" - def explain = + def explain(using Context) = e"""|${cdef.name} must have at least one parameter list, if you would rather |have a singleton representation of ${cdef.name}, use a "${hl("case object")}". |Or, add an explicit ${hl("()")} as a parameter list to ${cdef.name}.""" @@ -157,7 +158,7 @@ class AnonymousFunctionMissingParamType(param: untpd.ValDef, pt: Type) (using Context) extends TypeMsg(AnonymousFunctionMissingParamTypeID) { - def msg = { + def msg(using Context) = { val ofFun = if param.name.is(WildcardParamName) || (MethodType.syntheticParamNames(tree.args.length + 1) contains param.name) @@ -173,13 +174,13 @@ extends TypeMsg(AnonymousFunctionMissingParamTypeID) { |I could not infer the type of the parameter ${param.name}$ofFun.$inferred""" } - def explain = "" + def explain(using Context) = "" } class WildcardOnTypeArgumentNotAllowedOnNew()(using Context) extends SyntaxMsg(WildcardOnTypeArgumentNotAllowedOnNewID) { - def msg = "Type argument must be fully defined" - def explain = + def msg(using Context) = "Type argument must be fully defined" + def explain(using Context) = val code1: String = """ |object TyperDemo { @@ -210,9 +211,9 @@ extends SyntaxMsg(WildcardOnTypeArgumentNotAllowedOnNewID) { // Type Errors ------------------------------------------------------------ // class DuplicateBind(bind: untpd.Bind, tree: untpd.CaseDef)(using Context) extends NamingMsg(DuplicateBindID) { - def msg = e"duplicate pattern variable: ${bind.name}" + def msg(using Context) = e"duplicate pattern variable: ${bind.name}" - def explain = { + def explain(using Context) = { val pat = tree.pat.show val guard = tree.guard match case untpd.EmptyTree => "" @@ -235,8 +236,8 @@ extends NamingMsg(DuplicateBindID) { class MissingIdent(tree: untpd.Ident, treeKind: String, val name: Name)(using Context) extends NotFoundMsg(MissingIdentID) { - def msg = e"Not found: $treeKind$name" - def explain = { + def msg(using Context) = e"Not found: $treeKind$name" + def explain(using Context) = { e"""|The identifier for `$treeKind$name` is not bound, that is, |no declaration for this identifier can be found. |That can happen, for example, if `$name` or its declaration has either been @@ -247,7 +248,7 @@ extends NotFoundMsg(MissingIdentID) { class TypeMismatch(found: Type, expected: Type, inTree: Option[untpd.Tree], addenda: => String*)(using Context) extends TypeMismatchMsg(found, expected)(TypeMismatchID): - def msg = + def msg(using Context) = // replace constrained TypeParamRefs and their typevars by their bounds where possible // and the bounds are not f-bounds. // The idea is that if the bounds are also not-subtypes of each other to report @@ -281,21 +282,19 @@ class TypeMismatch(found: Type, expected: Type, inTree: Option[untpd.Tree], ad val (found2, expected2) = if (found1 frozen_<:< expected1) || reported.fbounded then (found, expected) else (found1, expected1) - val postScript = addenda.find(!_.isEmpty) match - case Some(p) => p - case None => - if expected.isTopType || found.isBottomType - then "" - else ctx.typer.importSuggestionAddendum(ViewProto(found.widen, expected)) - val (where, printCtx) = Formatting.disambiguateTypes(found2, expected2) - val whereSuffix = if (where.isEmpty) where else s"\n\n$where" - val (foundStr, expectedStr) = Formatting.typeDiff(found2, expected2)(using printCtx) - s"""|Found: $foundStr - |Required: $expectedStr""".stripMargin - + whereSuffix + postScript + val (foundStr, expectedStr) = Formatting.typeDiff(found2, expected2) + i"""|Found: $foundStr + |Required: $expectedStr""" end msg - override def explain = + override def msgPostscript(using Context) = + def importSuggestions = + if expected.isTopType || found.isBottomType then "" + else ctx.typer.importSuggestionAddendum(ViewProto(found.widen, expected)) + super.msgPostscript + ++ addenda.dropWhile(_.isEmpty).headOption.getOrElse(importSuggestions) + + override def explain(using Context) = val treeStr = inTree.map(x => s"\nTree: ${x.show}").getOrElse("") treeStr + "\n" + super.explain @@ -305,7 +304,7 @@ class NotAMember(site: Type, val name: Name, selected: String, addendum: => Stri extends NotFoundMsg(NotAMemberID), ShowMatchTrace(site) { //println(i"site = $site, decls = ${site.decls}, source = ${site.typeSymbol.sourceFile}") //DEBUG - def msg = { + def msg(using Context) = { import core.Flags._ val maxDist = 3 // maximal number of differences to be considered for a hint val missing = name.show @@ -374,14 +373,14 @@ extends NotFoundMsg(NotAMemberID), ShowMatchTrace(site) { ex"$selected $name is not a member of ${site.widen}$finalAddendum" } - def explain = "" + def explain(using Context) = "" } class EarlyDefinitionsNotSupported()(using Context) extends SyntaxMsg(EarlyDefinitionsNotSupportedID) { - def msg = "Early definitions are not supported; use trait parameters instead" + def msg(using Context) = "Early definitions are not supported; use trait parameters instead" - def explain = { + def explain(using Context) = { val code1 = """|trait Logging { | val f: File @@ -424,9 +423,9 @@ extends SyntaxMsg(EarlyDefinitionsNotSupportedID) { class TopLevelImplicitClass(cdef: untpd.TypeDef)(using Context) extends SyntaxMsg(TopLevelImplicitClassID) { - def msg = e"""An ${hl("implicit class")} may not be top-level""" + def msg(using Context) = e"""An ${hl("implicit class")} may not be top-level""" - def explain = { + def explain(using Context) = { val TypeDef(name, impl @ Template(constr0, parents, self, _)) = cdef: @unchecked val exampleArgs = if(constr0.termParamss.isEmpty) "..." @@ -452,9 +451,9 @@ extends SyntaxMsg(TopLevelImplicitClassID) { class ImplicitCaseClass(cdef: untpd.TypeDef)(using Context) extends SyntaxMsg(ImplicitCaseClassID) { - def msg = e"""A ${hl("case class")} may not be defined as ${hl("implicit")}""" + def msg(using Context) = e"""A ${hl("case class")} may not be defined as ${hl("implicit")}""" - def explain = + def explain(using Context) = e"""|Implicit classes may not be case classes. Instead use a plain class: | |implicit class ${cdef.name}... @@ -464,8 +463,8 @@ extends SyntaxMsg(ImplicitCaseClassID) { class ImplicitClassPrimaryConstructorArity()(using Context) extends SyntaxMsg(ImplicitClassPrimaryConstructorArityID){ - def msg = "Implicit classes must accept exactly one primary constructor parameter" - def explain = { + def msg(using Context) = "Implicit classes must accept exactly one primary constructor parameter" + def explain(using Context) = { val example = "implicit class RichDate(date: java.util.Date)" e"""Implicit classes may only take one non-implicit argument in their constructor. For example: | @@ -479,9 +478,9 @@ extends SyntaxMsg(ImplicitClassPrimaryConstructorArityID){ class ObjectMayNotHaveSelfType(mdef: untpd.ModuleDef)(using Context) extends SyntaxMsg(ObjectMayNotHaveSelfTypeID) { - def msg = e"""${hl("object")}s must not have a self ${hl("type")}""" + def msg(using Context) = e"""${hl("object")}s must not have a self ${hl("type")}""" - def explain = { + def explain(using Context) = { val untpd.ModuleDef(name, tmpl) = mdef val ValDef(_, selfTpt, _) = tmpl.self e"""|${hl("object")}s must not have a self ${hl("type")}: @@ -496,9 +495,9 @@ extends SyntaxMsg(ObjectMayNotHaveSelfTypeID) { class RepeatedModifier(modifier: String)(implicit ctx:Context) extends SyntaxMsg(RepeatedModifierID) { - def msg = e"""Repeated modifier $modifier""" + def msg(using Context) = e"""Repeated modifier $modifier""" - def explain = { + def explain(using Context) = { val code1 = "private private val Origin = Point(0, 0)" val code2 = "private final val Origin = Point(0, 0)" e"""This happens when you accidentally specify the same modifier twice. @@ -517,8 +516,8 @@ extends SyntaxMsg(RepeatedModifierID) { class InterpolatedStringError()(implicit ctx:Context) extends SyntaxMsg(InterpolatedStringErrorID) { - def msg = "Error in interpolated string: identifier or block expected" - def explain = { + def msg(using Context) = "Error in interpolated string: identifier or block expected" + def explain(using Context) = { val code1 = "s\"$new Point(0, 0)\"" val code2 = "s\"${new Point(0, 0)}\"" e"""|This usually happens when you forget to place your expressions inside curly braces. @@ -534,8 +533,8 @@ extends SyntaxMsg(InterpolatedStringErrorID) { class UnboundPlaceholderParameter()(implicit ctx:Context) extends SyntaxMsg(UnboundPlaceholderParameterID) { - def msg = e"""Unbound placeholder parameter; incorrect use of ${hl("_")}""" - def explain = + def msg(using Context) = e"""Unbound placeholder parameter; incorrect use of ${hl("_")}""" + def explain(using Context) = e"""|The ${hl("_")} placeholder syntax was used where it could not be bound. |Consider explicitly writing the variable binding. | @@ -580,16 +579,16 @@ extends SyntaxMsg(UnboundPlaceholderParameterID) { class IllegalStartSimpleExpr(illegalToken: String)(using Context) extends SyntaxMsg(IllegalStartSimpleExprID) { - def msg = e"expression expected but ${Red(illegalToken)} found" - def explain = { + def msg(using Context) = e"expression expected but ${Red(illegalToken)} found" + def explain(using Context) = { e"""|An expression cannot start with ${Red(illegalToken)}.""" } } class MissingReturnType()(implicit ctx:Context) extends SyntaxMsg(MissingReturnTypeID) { - def msg = "Missing return type" - def explain = + def msg(using Context) = "Missing return type" + def explain(using Context) = e"""|An abstract declaration must have a return type. For example: | |trait Shape: @@ -598,8 +597,8 @@ extends SyntaxMsg(MissingReturnTypeID) { class MissingReturnTypeWithReturnStatement(method: Symbol)(using Context) extends SyntaxMsg(MissingReturnTypeWithReturnStatementID) { - def msg = e"$method has a return statement; it needs a result type" - def explain = + def msg(using Context) = e"$method has a return statement; it needs a result type" + def explain(using Context) = e"""|If a method contains a ${hl("return")} statement, it must have an |explicit return type. For example: | @@ -608,9 +607,9 @@ extends SyntaxMsg(MissingReturnTypeWithReturnStatementID) { class YieldOrDoExpectedInForComprehension()(using Context) extends SyntaxMsg(YieldOrDoExpectedInForComprehensionID) { - def msg = e"${hl("yield")} or ${hl("do")} expected" + def msg(using Context) = e"${hl("yield")} or ${hl("do")} expected" - def explain = + def explain(using Context) = e"""|When the enumerators in a for comprehension are not placed in parentheses or |braces, a ${hl("do")} or ${hl("yield")} statement is required after the enumerators |section of the comprehension. @@ -641,9 +640,9 @@ extends SyntaxMsg(YieldOrDoExpectedInForComprehensionID) { class ProperDefinitionNotFound()(using Context) extends Message(ProperDefinitionNotFoundID) { def kind = MessageKind.DocComment - def msg = e"""Proper definition was not found in ${hl("@usecase")}""" + def msg(using Context) = e"""Proper definition was not found in ${hl("@usecase")}""" - def explain = { + def explain(using Context) = { val noUsecase = "def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That" @@ -679,9 +678,9 @@ extends Message(ProperDefinitionNotFoundID) { class ByNameParameterNotSupported(tpe: untpd.Tree)(using Context) extends SyntaxMsg(ByNameParameterNotSupportedID) { - def msg = e"By-name parameter type ${tpe} not allowed here." + def msg(using Context) = e"By-name parameter type ${tpe} not allowed here." - def explain = + def explain(using Context) = e"""|By-name parameters act like functions that are only evaluated when referenced, |allowing for lazy evaluation of a parameter. | @@ -707,7 +706,7 @@ extends SyntaxMsg(WrongNumberOfTypeArgsID) { private val actualCount = actual.length private val msgPrefix = if (actualCount > expectedCount) "Too many" else "Not enough" - def msg = + def msg(using Context) = val expectedArgString = expectedArgs .map(_.paramName.unexpandedName.show) .mkString("[", ", ", "]") @@ -721,7 +720,7 @@ extends SyntaxMsg(WrongNumberOfTypeArgsID) { |expected: $expectedArgString |actual: $actualArgString""" - def explain = { + def explain(using Context) = { val tooManyTypeParams = """|val tuple2: (Int, String) = (1, "one") |val list: List[(Int, String)] = List(tuple2)""".stripMargin @@ -743,8 +742,8 @@ extends SyntaxMsg(WrongNumberOfTypeArgsID) { class IllegalVariableInPatternAlternative(name: Name)(using Context) extends SyntaxMsg(IllegalVariableInPatternAlternativeID) { - def msg = e"Illegal variable $name in pattern alternative" - def explain = { + def msg(using Context) = e"Illegal variable $name in pattern alternative" + def explain(using Context) = { val varInAlternative = """|def g(pair: (Int,Int)): Int = pair match { | case (1, n) | (n, 1) => n @@ -771,8 +770,8 @@ extends SyntaxMsg(IllegalVariableInPatternAlternativeID) { class IdentifierExpected(identifier: String)(using Context) extends SyntaxMsg(IdentifierExpectedID) { - def msg = "identifier expected" - def explain = { + def msg(using Context) = "identifier expected" + def explain(using Context) = { val wrongIdentifier = e"def foo: $identifier = {...}" val validIdentifier = e"def foo = {...}" e"""|An identifier expected, but $identifier found. This could be because @@ -791,8 +790,8 @@ extends SyntaxMsg(IdentifierExpectedID) { class AuxConstructorNeedsNonImplicitParameter()(implicit ctx:Context) extends SyntaxMsg(AuxConstructorNeedsNonImplicitParameterID) { - def msg = "Auxiliary constructor needs non-implicit parameter list" - def explain = + def msg(using Context) = "Auxiliary constructor needs non-implicit parameter list" + def explain(using Context) = e"""|Only the primary constructor is allowed an ${hl("implicit")} parameter list; |auxiliary constructors need non-implicit parameter lists. When a primary |constructor has an implicit argslist, auxiliary constructors that call the @@ -806,8 +805,8 @@ extends SyntaxMsg(AuxConstructorNeedsNonImplicitParameterID) { class IllegalLiteral()(using Context) extends SyntaxMsg(IllegalLiteralID) { - def msg = "Illegal literal" - def explain = + def msg(using Context) = "Illegal literal" + def explain(using Context) = e"""|Available literals can be divided into several groups: | - Integer literals: 0, 21, 0xFFFFFFFF, -42L | - Floating Point Literals: 0.0, 1e30f, 3.14159f, 1.0e-100, .1 @@ -821,15 +820,15 @@ extends SyntaxMsg(IllegalLiteralID) { class LossyWideningConstantConversion(sourceType: Type, targetType: Type)(using Context) extends Message(LossyWideningConstantConversionID): def kind = MessageKind.LossyConversion - def msg = e"""|Widening conversion from $sourceType to $targetType loses precision. + def msg(using Context) = e"""|Widening conversion from $sourceType to $targetType loses precision. |Write `.to$targetType` instead.""" - def explain = "" + def explain(using Context) = "" class PatternMatchExhaustivity(uncoveredFn: => String, hasMore: Boolean)(using Context) extends Message(PatternMatchExhaustivityID) { def kind = MessageKind.PatternMatchExhaustivity lazy val uncovered = uncoveredFn - def msg = + def msg(using Context) = val addendum = if hasMore then "(More unmatched cases are elided)" else "" e"""|${hl("match")} may not be exhaustive. | @@ -837,7 +836,7 @@ extends Message(PatternMatchExhaustivityID) { |$addendum""" - def explain = + def explain(using Context) = e"""|There are several ways to make the match exhaustive: | - Add missing cases as shown in the warning | - If an extractor always return ${hl("Some(...)")}, write ${hl("Some[X]")} for its return type @@ -847,8 +846,8 @@ extends Message(PatternMatchExhaustivityID) { class UncheckedTypePattern(msgFn: => String)(using Context) extends PatternMatchMsg(UncheckedTypePatternID) { - def msg = msgFn - def explain = + def msg(using Context) = msgFn + def explain(using Context) = e"""|Type arguments and type refinements are erased during compile time, thus it's |impossible to check them at run-time. | @@ -859,24 +858,24 @@ class UncheckedTypePattern(msgFn: => String)(using Context) class MatchCaseUnreachable()(using Context) extends Message(MatchCaseUnreachableID) { def kind = MessageKind.MatchCaseUnreachable - def msg = "Unreachable case" - def explain = "" + def msg(using Context) = "Unreachable case" + def explain(using Context) = "" } class MatchCaseOnlyNullWarning()(using Context) extends PatternMatchMsg(MatchCaseOnlyNullWarningID) { - def msg = e"""Unreachable case except for ${hl("null")} (if this is intentional, consider writing ${hl("case null =>")} instead).""" - def explain = "" + def msg(using Context) = e"""Unreachable case except for ${hl("null")} (if this is intentional, consider writing ${hl("case null =>")} instead).""" + def explain(using Context) = "" } class MatchableWarning(tp: Type, pattern: Boolean)(using Context) extends TypeMsg(MatchableWarningID) { - def msg = + def msg(using Context) = val kind = if pattern then "pattern selector" else "value" e"""${kind} should be an instance of Matchable,, |but it has unmatchable type $tp instead""" - def explain = + def explain(using Context) = if pattern then e"""A value of type $tp cannot be the selector of a match expression |since it is not constrained to be `Matchable`. Matching on unconstrained @@ -897,8 +896,8 @@ extends TypeMsg(MatchableWarningID) { class SeqWildcardPatternPos()(using Context) extends SyntaxMsg(SeqWildcardPatternPosID) { - def msg = e"""${hl("*")} can be used only for last argument""" - def explain = { + def msg(using Context) = e"""${hl("*")} can be used only for last argument""" + def explain(using Context) = { val code = """def sumOfTheFirstTwo(list: List[Int]): Int = list match { | case List(first, second, x*) => first + second @@ -920,8 +919,8 @@ extends SyntaxMsg(SeqWildcardPatternPosID) { class IllegalStartOfSimplePattern()(using Context) extends SyntaxMsg(IllegalStartOfSimplePatternID) { - def msg = "pattern expected" - def explain = { + def msg(using Context) = "pattern expected" + def explain(using Context) = { val sipCode = """def f(x: Int, y: Int) = x match { | case `y` => ... @@ -999,16 +998,16 @@ extends SyntaxMsg(IllegalStartOfSimplePatternID) { class PkgDuplicateSymbol(existing: Symbol)(using Context) extends NamingMsg(PkgDuplicateSymbolID) { - def msg = e"Trying to define package with same name as $existing" - def explain = "" + def msg(using Context) = e"Trying to define package with same name as $existing" + def explain(using Context) = "" } class ExistentialTypesNoLongerSupported()(using Context) extends SyntaxMsg(ExistentialTypesNoLongerSupportedID) { - def msg = + def msg(using Context) = e"""|Existential types are no longer supported - |use a wildcard or dependent type instead""" - def explain = + def explain(using Context) = e"""|The use of existential types is no longer supported. | |You should use a wildcard or dependent type instead. @@ -1027,8 +1026,8 @@ extends SyntaxMsg(ExistentialTypesNoLongerSupportedID) { class UnboundWildcardType()(using Context) extends SyntaxMsg(UnboundWildcardTypeID) { - def msg = "Unbound wildcard type" - def explain = + def msg(using Context) = "Unbound wildcard type" + def explain(using Context) = e"""|The wildcard type syntax (${hl("_")}) was used where it could not be bound. |Replace ${hl("_")} with a non-wildcard type. If the type doesn't matter, |try replacing ${hl("_")} with ${hl("Any")}. @@ -1071,9 +1070,9 @@ extends SyntaxMsg(UnboundWildcardTypeID) { class OverridesNothing(member: Symbol)(using Context) extends DeclarationMsg(OverridesNothingID) { - def msg = e"""${member} overrides nothing""" + def msg(using Context) = e"""${member} overrides nothing""" - def explain = + def explain(using Context) = e"""|There must be a field or method with the name ${member.name} in a super |class of ${member.owner} to override it. Did you misspell it? |Are you extending the right classes? @@ -1082,13 +1081,13 @@ extends DeclarationMsg(OverridesNothingID) { class OverridesNothingButNameExists(member: Symbol, existing: List[Denotations.SingleDenotation])(using Context) extends DeclarationMsg(OverridesNothingButNameExistsID) { - def msg = + def msg(using Context) = val what = if !existing.exists(_.symbol.hasTargetName(member.targetName)) then "target name" else "signature" e"""${member} has a different $what than the overridden declaration""" - def explain = + def explain(using Context) = val existingDecl: String = existing.map(_.showDcl).mkString(" \n") e"""|There must be a non-final field or method with the name ${member.name} and the |same parameter list in a super class of ${member.owner} to override it. @@ -1104,8 +1103,9 @@ extends DeclarationMsg(OverridesNothingButNameExistsID) { class OverrideError( core: Context ?=> String, base: Type, member: Symbol, other: Symbol, - memberTp: Type, otherTp: Type)(using Context) extends DeclarationMsg(OverrideErrorID): - def msg = + memberTp: Type, otherTp: Type)(using Context) +extends DeclarationMsg(OverrideErrorID), NoDisambiguation: + def msg(using Context) = val isConcreteOverAbstract = (other.owner isSubClass member.owner) && other.is(Deferred) && !member.is(Deferred) def addendum = @@ -1118,14 +1118,14 @@ class OverrideError( | ${err.infoString(member, base, showLocation = member.owner != base.typeSymbol)} $core$addendum""" override def canExplain = memberTp.exists && otherTp.exists - def explain = + def explain(using Context) = if canExplain then err.whyNoMatchStr(memberTp, otherTp) else "" class ForwardReferenceExtendsOverDefinition(value: Symbol, definition: Symbol)(using Context) extends ReferenceMsg(ForwardReferenceExtendsOverDefinitionID) { - def msg = e"${definition.name} is a forward reference extending over the definition of ${value.name}" + def msg(using Context) = e"${definition.name} is a forward reference extending over the definition of ${value.name}" - def explain = + def explain(using Context) = e"""|${definition.name} is used before you define it, and the definition of ${value.name} |appears between that use and the definition of ${definition.name}. | @@ -1144,13 +1144,13 @@ extends SyntaxMsg(ExpectedTokenButFoundID) { private def foundText = Tokens.showToken(found) - def msg = + def msg(using Context) = val expectedText = if (Tokens.isIdentifier(expected)) "an identifier" else Tokens.showToken(expected) e"""${expectedText} expected, but ${foundText} found""" - def explain = + def explain(using Context) = if (Tokens.isIdentifier(expected) && Tokens.isKeyword(found)) s""" |If you want to use $foundText as identifier, you may put it in backticks: `${Tokens.tokenString(found)}`.""".stripMargin @@ -1160,11 +1160,11 @@ extends SyntaxMsg(ExpectedTokenButFoundID) { class MixedLeftAndRightAssociativeOps(op1: Name, op2: Name, op2LeftAssoc: Boolean)(using Context) extends SyntaxMsg(MixedLeftAndRightAssociativeOpsID) { - def msg = + def msg(using Context) = val op1Asso: String = if (op2LeftAssoc) "which is right-associative" else "which is left-associative" val op2Asso: String = if (op2LeftAssoc) "which is left-associative" else "which is right-associative" e"${op1} (${op1Asso}) and ${op2} ($op2Asso) have same precedence and may not be mixed" - def explain = + def explain(using Context) = s"""|The operators ${op1} and ${op2} are used as infix operators in the same expression, |but they bind to different sides: |${op1} is applied to the operand to its ${if (op2LeftAssoc) "right" else "left"} @@ -1195,8 +1195,8 @@ extends SyntaxMsg(MixedLeftAndRightAssociativeOpsID) { class CantInstantiateAbstractClassOrTrait(cls: Symbol, isTrait: Boolean)(using Context) extends TypeMsg(CantInstantiateAbstractClassOrTraitID) { private val traitOrAbstract = if (isTrait) "a trait" else "abstract" - def msg = e"""${cls.name} is ${traitOrAbstract}; it cannot be instantiated""" - def explain = + def msg(using Context) = e"""${cls.name} is ${traitOrAbstract}; it cannot be instantiated""" + def explain(using Context) = e"""|Abstract classes and traits need to be extended by a concrete class or object |to make their functionality accessible. | @@ -1211,16 +1211,16 @@ extends TypeMsg(CantInstantiateAbstractClassOrTraitID) { } class UnreducibleApplication(tycon: Type)(using Context) extends TypeMsg(UnreducibleApplicationID): - def msg = e"unreducible application of higher-kinded type $tycon to wildcard arguments" - def explain = + def msg(using Context) = e"unreducible application of higher-kinded type $tycon to wildcard arguments" + def explain(using Context) = e"""|An abstract type constructor cannot be applied to wildcard arguments. |Such applications are equivalent to existential types, which are not |supported in Scala 3.""" class OverloadedOrRecursiveMethodNeedsResultType(cycleSym: Symbol)(using Context) extends CyclicMsg(OverloadedOrRecursiveMethodNeedsResultTypeID) { - def msg = e"""Overloaded or recursive $cycleSym needs return type""" - def explain = + def msg(using Context) = e"""Overloaded or recursive $cycleSym needs return type""" + def explain(using Context) = e"""Case 1: $cycleSym is overloaded |If there are multiple methods named $cycleSym and at least one definition of |it calls another, you need to specify the calling method's return type. @@ -1233,18 +1233,18 @@ extends CyclicMsg(OverloadedOrRecursiveMethodNeedsResultTypeID) { class RecursiveValueNeedsResultType(cycleSym: Symbol)(using Context) extends CyclicMsg(RecursiveValueNeedsResultTypeID) { - def msg = e"""Recursive $cycleSym needs type""" - def explain = + def msg(using Context) = e"""Recursive $cycleSym needs type""" + def explain(using Context) = e"""The definition of $cycleSym is recursive and you need to specify its type. |""" } class CyclicReferenceInvolving(denot: SymDenotation)(using Context) extends CyclicMsg(CyclicReferenceInvolvingID) { - def msg = + def msg(using Context) = val where = if denot.exists then s" involving $denot" else "" e"Cyclic reference$where" - def explain = + def explain(using Context) = e"""|$denot is declared as part of a cycle which makes it impossible for the |compiler to decide upon ${denot.name}'s type. |To avoid this error, try giving ${denot.name} an explicit type. @@ -1253,8 +1253,8 @@ extends CyclicMsg(CyclicReferenceInvolvingID) { class CyclicReferenceInvolvingImplicit(cycleSym: Symbol)(using Context) extends CyclicMsg(CyclicReferenceInvolvingImplicitID) { - def msg = e"""Cyclic reference involving implicit $cycleSym""" - def explain = + def msg(using Context) = e"""Cyclic reference involving implicit $cycleSym""" + def explain(using Context) = e"""|$cycleSym is declared as part of a cycle which makes it impossible for the |compiler to decide upon ${cycleSym.name}'s type. |This might happen when the right hand-side of $cycleSym's definition involves an implicit search. @@ -1264,7 +1264,7 @@ extends CyclicMsg(CyclicReferenceInvolvingImplicitID) { class SkolemInInferred(tree: tpd.Tree, pt: Type, argument: tpd.Tree)(using Context) extends TypeMsg(SkolemInInferredID): - def msg = + def msg(using Context) = def argStr = if argument.isEmpty then "" else i" from argument of type ${argument.tpe.widen}" @@ -1274,7 +1274,7 @@ extends TypeMsg(SkolemInInferredID): |But the part corresponding to `` is not a reference that can be generated. |This might be because resolution yielded as given instance a function that is not |known to be total and side-effect free.""" - def explain = + def explain(using Context) = e"""The part of given resolution that corresponds to `` produced a term that |is not a stable reference. Therefore a given instance could not be generated. | @@ -1283,8 +1283,8 @@ extends TypeMsg(SkolemInInferredID): class SuperQualMustBeParent(qual: untpd.Ident, cls: ClassSymbol)(using Context) extends ReferenceMsg(SuperQualMustBeParentID) { - def msg = e"""|$qual does not name a parent of $cls""" - def explain = + def msg(using Context) = e"""|$qual does not name a parent of $cls""" + def explain(using Context) = val parents: Seq[String] = (cls.info.parents map (_.typeSymbol.name.show)).sorted e"""|When a qualifier ${hl("T")} is used in a ${hl("super")} prefix of the form ${hl("C.super[T]")}, |${hl("T")} must be a parent type of ${hl("C")}. @@ -1296,8 +1296,8 @@ extends ReferenceMsg(SuperQualMustBeParentID) { class VarArgsParamMustComeLast()(using Context) extends SyntaxMsg(VarArgsParamMustComeLastID) { - def msg = e"""${hl("varargs")} parameter must come last""" - def explain = + def msg(using Context) = e"""${hl("varargs")} parameter must come last""" + def explain(using Context) = e"""|The ${hl("varargs")} field must be the last field in the method signature. |Attempting to define a field in a method signature after a ${hl("varargs")} field is an error. |""" @@ -1306,7 +1306,7 @@ extends SyntaxMsg(VarArgsParamMustComeLastID) { import typer.Typer.BindingPrec class AmbiguousReference(name: Name, newPrec: BindingPrec, prevPrec: BindingPrec, prevCtx: Context)(using Context) - extends ReferenceMsg(AmbiguousReferenceID) { + extends ReferenceMsg(AmbiguousReferenceID), NoDisambiguation { /** A string which explains how something was bound; Depending on `prec` this is either * imported by @@ -1327,12 +1327,12 @@ class AmbiguousReference(name: Name, newPrec: BindingPrec, prevPrec: BindingPrec ex"""$howVisible$qualifier in ${e"${whereFound.owner}"}""" } - def msg = + def msg(using Context) = i"""|Reference to ${e"$name"} is ambiguous, |it is both ${bindingString(newPrec, ctx)} |and ${bindingString(prevPrec, prevCtx, " subsequently")}""" - def explain = + def explain(using Context) = e"""|The compiler can't decide which of the possible choices you |are referencing with $name: A definition of lower precedence |in an inner scope, or a definition with higher precedence in @@ -1358,14 +1358,14 @@ extends TypeMsg(MethodDoesNotTakeParametersId) { else sym recur(tree) - def msg = { + def msg(using Context) = { val more = if (tree.isInstanceOf[tpd.Apply]) " more" else "" val meth = methodSymbol val methStr = if (meth.exists) meth.showLocated else "expression" e"$methStr does not take$more parameters" } - def explain = { + def explain(using Context) = { val isNullary = methodSymbol.info.isInstanceOf[ExprType] val addendum = if (isNullary) "\nNullary methods may not be called with parenthesis" @@ -1378,12 +1378,12 @@ extends TypeMsg(MethodDoesNotTakeParametersId) { class AmbiguousOverload(tree: tpd.Tree, val alternatives: List[SingleDenotation], pt: Type, addendum: String = "")( implicit ctx: Context) -extends ReferenceMsg(AmbiguousOverloadID) { +extends ReferenceMsg(AmbiguousOverloadID), NoDisambiguation { private def all = if (alternatives.length == 2) "both" else "all" - def msg = + def msg(using Context) = e"""|Ambiguous overload. The ${err.overloadedAltsStr(alternatives)} |$all match ${err.expectedTypeStr(pt)}$addendum""" - def explain = + def explain(using Context) = e"""|There are ${alternatives.length} methods that could be referenced as the compiler knows too little |about the expected type. |You may specify the expected type e.g. by @@ -1394,8 +1394,8 @@ extends ReferenceMsg(AmbiguousOverloadID) { class ReassignmentToVal(name: Name)(using Context) extends TypeMsg(ReassignmentToValID) { - def msg = e"""Reassignment to val $name""" - def explain = + def msg(using Context) = e"""Reassignment to val $name""" + def explain(using Context) = e"""|You can not assign a new value to $name as values can't be changed. |Keep in mind that every statement has a value, so you may e.g. use | ${hl("val")} $name ${hl("= if (condition) 2 else 5")} @@ -1411,8 +1411,8 @@ class TypeDoesNotTakeParameters(tpe: Type, params: List[untpd.Tree])(using Conte if tpe.typeSymbol.isAllOf(Provisional | TypeParam) then "\n(Note that F-bounds of type parameters may not be type lambdas)" else "" - def msg = e"$tpe does not take type parameters$fboundsAddendum" - def explain = + def msg(using Context) = e"$tpe does not take type parameters$fboundsAddendum" + def explain(using Context) = val ps = if (params.size == 1) s"a type parameter ${params.head}" else s"type parameters ${params.map(_.show).mkString(", ")}" @@ -1424,8 +1424,8 @@ class TypeDoesNotTakeParameters(tpe: Type, params: List[untpd.Tree])(using Conte class VarValParametersMayNotBeCallByName(name: TermName, mutable: Boolean)(using Context) extends SyntaxMsg(VarValParametersMayNotBeCallByNameID) { def varOrVal = if (mutable) e"${hl("var")}" else e"${hl("val")}" - def msg = s"$varOrVal parameters may not be call-by-name" - def explain = + def msg(using Context) = s"$varOrVal parameters may not be call-by-name" + def explain(using Context) = e"""${hl("var")} and ${hl("val")} parameters of classes and traits may no be call-by-name. In case you |want the parameter to be evaluated on demand, consider making it just a parameter |and a ${hl("def")} in the class such as @@ -1437,26 +1437,26 @@ class VarValParametersMayNotBeCallByName(name: TermName, mutable: Boolean)(using class MissingTypeParameterFor(tpe: Type)(using Context) extends SyntaxMsg(MissingTypeParameterForID) { - def msg = + def msg(using Context) = if (tpe.derivesFrom(defn.AnyKindClass)) e"${tpe} cannot be used as a value type" else e"Missing type parameter for ${tpe}" - def explain = "" + def explain(using Context) = "" } class MissingTypeParameterInTypeApp(tpe: Type)(using Context) extends TypeMsg(MissingTypeParameterInTypeAppID) { def numParams = tpe.typeParams.length def parameters = if (numParams == 1) "parameter" else "parameters" - def msg = e"Missing type $parameters for $tpe" - def explain = e"A fully applied type is expected but $tpe takes $numParams $parameters" + def msg(using Context) = e"Missing type $parameters for $tpe" + def explain(using Context) = e"A fully applied type is expected but $tpe takes $numParams $parameters" } class MissingArgument(pname: Name, methString: String)(using Context) extends TypeMsg(MissingArgumentID): - def msg = + def msg(using Context) = if pname.firstPart contains '$' then s"not enough arguments for $methString" else s"missing argument for parameter $pname of $methString" - def explain = "" + def explain(using Context) = "" class DoesNotConformToBound(tpe: Type, which: String, bound: Type)(using Context) extends TypeMismatchMsg( @@ -1466,7 +1466,7 @@ class DoesNotConformToBound(tpe: Type, which: String, bound: Type)(using Context case TypeBounds(lo, hi) => lo ne hi case _ => false override def canExplain = !isBounds - def msg = + def msg(using Context) = if isBounds then e"Type argument ${tpe} does not overlap with $which bound $bound" else @@ -1476,19 +1476,19 @@ class DoesNotConformToSelfType(category: String, selfType: Type, cls: Symbol, otherSelf: Type, relation: String, other: Symbol)( implicit ctx: Context) extends TypeMismatchMsg(selfType, otherSelf)(DoesNotConformToSelfTypeID) { - def msg = e"""$category: self type $selfType of $cls does not conform to self type $otherSelf + def msg(using Context) = e"""$category: self type $selfType of $cls does not conform to self type $otherSelf |of $relation $other""" } class DoesNotConformToSelfTypeCantBeInstantiated(tp: Type, selfType: Type)( implicit ctx: Context) extends TypeMismatchMsg(tp, selfType)(DoesNotConformToSelfTypeCantBeInstantiatedID) { - def msg = e"""$tp does not conform to its self type $selfType; cannot be instantiated""" + def msg(using Context) = e"""$tp does not conform to its self type $selfType; cannot be instantiated""" } class IllegalParameterInit(found: Type, expected: Type, param: Symbol, cls: Symbol)(using Context) extends TypeMismatchMsg(found, expected)(IllegalParameterInitID): - def msg = + def msg(using Context) = e"""illegal parameter initialization of $param. | | The argument passed for $param has type: $found @@ -1497,66 +1497,66 @@ class IllegalParameterInit(found: Type, expected: Type, param: Symbol, cls: Symb class AbstractMemberMayNotHaveModifier(sym: Symbol, flag: FlagSet)( implicit ctx: Context) extends SyntaxMsg(AbstractMemberMayNotHaveModifierID) { - def msg = e"""${hl("abstract")} $sym may not have `${flag.flagsString}` modifier""" - def explain = "" + def msg(using Context) = e"""${hl("abstract")} $sym may not have `${flag.flagsString}` modifier""" + def explain(using Context) = "" } class TypesAndTraitsCantBeImplicit()(using Context) extends SyntaxMsg(TypesAndTraitsCantBeImplicitID) { - def msg = e"""${hl("implicit")} modifier cannot be used for types or traits""" - def explain = "" + def msg(using Context) = e"""${hl("implicit")} modifier cannot be used for types or traits""" + def explain(using Context) = "" } class OnlyClassesCanBeAbstract(sym: Symbol)( implicit ctx: Context) extends SyntaxMsg(OnlyClassesCanBeAbstractID) { - def explain = "" - def msg = e"""${hl("abstract")} modifier can be used only for classes; it should be omitted for abstract members""" + def explain(using Context) = "" + def msg(using Context) = e"""${hl("abstract")} modifier can be used only for classes; it should be omitted for abstract members""" } class AbstractOverrideOnlyInTraits(sym: Symbol)( implicit ctx: Context) extends SyntaxMsg(AbstractOverrideOnlyInTraitsID) { - def msg = e"""${hl("abstract override")} modifier only allowed for members of traits""" - def explain = "" + def msg(using Context) = e"""${hl("abstract override")} modifier only allowed for members of traits""" + def explain(using Context) = "" } class TraitsMayNotBeFinal(sym: Symbol)( implicit ctx: Context) extends SyntaxMsg(TraitsMayNotBeFinalID) { - def msg = e"""$sym may not be ${hl("final")}""" - def explain = + def msg(using Context) = e"""$sym may not be ${hl("final")}""" + def explain(using Context) = "A trait can never be final since it is abstract and must be extended to be useful." } class NativeMembersMayNotHaveImplementation(sym: Symbol)( implicit ctx: Context) extends SyntaxMsg(NativeMembersMayNotHaveImplementationID) { - def msg = e"""${hl("@native")} members may not have an implementation""" - def explain = "" + def msg(using Context) = e"""${hl("@native")} members may not have an implementation""" + def explain(using Context) = "" } class TraitMayNotDefineNativeMethod(sym: Symbol)( implicit ctx: Context) extends SyntaxMsg(TraitMayNotDefineNativeMethodID) { - def msg = e"""A trait cannot define a ${hl("@native")} method.""" - def explain = "" + def msg(using Context) = e"""A trait cannot define a ${hl("@native")} method.""" + def explain(using Context) = "" } class OnlyClassesCanHaveDeclaredButUndefinedMembers(sym: Symbol)( implicit ctx: Context) extends SyntaxMsg(OnlyClassesCanHaveDeclaredButUndefinedMembersID) { - def msg = e"""Declaration of $sym not allowed here: only classes can have declared but undefined members""" - def explain = + def msg(using Context) = e"""Declaration of $sym not allowed here: only classes can have declared but undefined members""" + def explain(using Context) = if sym.is(Mutable) then "Note that variables need to be initialized to be defined." else "" } class CannotExtendAnyVal(sym: Symbol)(using Context) extends SyntaxMsg(CannotExtendAnyValID) { - def msg = e"""$sym cannot extend ${hl("AnyVal")}""" - def explain = + def msg(using Context) = e"""$sym cannot extend ${hl("AnyVal")}""" + def explain(using Context) = e"""Only classes (not traits) are allowed to extend ${hl("AnyVal")}, but traits may extend |${hl("Any")} to become ${Green("\"universal traits\"")} which may only have ${hl("def")} members. |Universal traits can be mixed into classes that extend ${hl("AnyVal")}. @@ -1565,24 +1565,24 @@ class CannotExtendAnyVal(sym: Symbol)(using Context) class CannotExtendJavaEnum(sym: Symbol)(using Context) extends SyntaxMsg(CannotExtendJavaEnumID) { - def msg = e"""$sym cannot extend ${hl("java.lang.Enum")}: only enums defined with the ${hl("enum")} syntax can""" - def explain = "" + def msg(using Context) = e"""$sym cannot extend ${hl("java.lang.Enum")}: only enums defined with the ${hl("enum")} syntax can""" + def explain(using Context) = "" } class CannotExtendContextFunction(sym: Symbol)(using Context) extends SyntaxMsg(CannotExtendFunctionID) { - def msg = e"""$sym cannot extend a context function class""" - def explain = "" + def msg(using Context) = e"""$sym cannot extend a context function class""" + def explain(using Context) = "" } class JavaEnumParentArgs(parent: Type)(using Context) extends TypeMsg(JavaEnumParentArgsID) { - def msg = e"""not enough arguments for constructor Enum: ${hl("(name: String, ordinal: Int)")}: ${hl(parent.show)}""" - def explain = "" + def msg(using Context) = e"""not enough arguments for constructor Enum: ${hl("(name: String, ordinal: Int)")}: ${hl(parent.show)}""" + def explain(using Context) = "" } class CannotHaveSameNameAs(sym: Symbol, cls: Symbol, reason: CannotHaveSameNameAs.Reason)(using Context) - extends SyntaxMsg(CannotHaveSameNameAsID) { + extends NamingMsg(CannotHaveSameNameAsID) { import CannotHaveSameNameAs._ def reasonMessage(using Context): String = reason match { case CannotBeOverridden => "class definitions cannot be overridden" @@ -1592,8 +1592,8 @@ class CannotHaveSameNameAs(sym: Symbol, cls: Symbol, reason: CannotHaveSameNameA |""".stripMargin } - def msg = e"""$sym cannot have the same name as ${cls.showLocated} -- """ + reasonMessage - def explain = "" + def msg(using Context) = e"""$sym cannot have the same name as ${cls.showLocated} -- """ + reasonMessage + def explain(using Context) = "" } object CannotHaveSameNameAs { sealed trait Reason @@ -1603,75 +1603,75 @@ object CannotHaveSameNameAs { class ValueClassesMayNotDefineInner(valueClass: Symbol, inner: Symbol)(using Context) extends SyntaxMsg(ValueClassesMayNotDefineInnerID) { - def msg = e"""Value classes may not define an inner class""" - def explain = "" + def msg(using Context) = e"""Value classes may not define an inner class""" + def explain(using Context) = "" } class ValueClassesMayNotDefineNonParameterField(valueClass: Symbol, field: Symbol)(using Context) extends SyntaxMsg(ValueClassesMayNotDefineNonParameterFieldID) { - def msg = e"""Value classes may not define non-parameter field""" - def explain = "" + def msg(using Context) = e"""Value classes may not define non-parameter field""" + def explain(using Context) = "" } class ValueClassesMayNotDefineASecondaryConstructor(valueClass: Symbol, constructor: Symbol)(using Context) extends SyntaxMsg(ValueClassesMayNotDefineASecondaryConstructorID) { - def msg = e"""Value classes may not define a secondary constructor""" - def explain = "" + def msg(using Context) = e"""Value classes may not define a secondary constructor""" + def explain(using Context) = "" } class ValueClassesMayNotContainInitalization(valueClass: Symbol)(using Context) extends SyntaxMsg(ValueClassesMayNotContainInitalizationID) { - def msg = e"""Value classes may not contain initialization statements""" - def explain = "" + def msg(using Context) = e"""Value classes may not contain initialization statements""" + def explain(using Context) = "" } class ValueClassesMayNotBeAbstract(valueClass: Symbol)(using Context) extends SyntaxMsg(ValueClassesMayNotBeAbstractID) { - def msg = e"""Value classes may not be ${hl("abstract")}""" - def explain = "" + def msg(using Context) = e"""Value classes may not be ${hl("abstract")}""" + def explain(using Context) = "" } class ValueClassesMayNotBeContainted(valueClass: Symbol)(using Context) extends SyntaxMsg(ValueClassesMayNotBeContaintedID) { private def localOrMember = if (valueClass.owner.isTerm) "local class" else "member of another class" - def msg = s"""Value classes may not be a $localOrMember""" - def explain = "" + def msg(using Context) = s"""Value classes may not be a $localOrMember""" + def explain(using Context) = "" } class ValueClassesMayNotWrapAnotherValueClass(valueClass: Symbol)(using Context) extends SyntaxMsg(ValueClassesMayNotWrapAnotherValueClassID) { - def msg = """A value class may not wrap another user-defined value class""" - def explain = "" + def msg(using Context) = """A value class may not wrap another user-defined value class""" + def explain(using Context) = "" } class ValueClassParameterMayNotBeAVar(valueClass: Symbol, param: Symbol)(using Context) extends SyntaxMsg(ValueClassParameterMayNotBeAVarID) { - def msg = e"""A value class parameter may not be a ${hl("var")}""" - def explain = + def msg(using Context) = e"""A value class parameter may not be a ${hl("var")}""" + def explain(using Context) = e"""A value class must have exactly one ${hl("val")} parameter.""" } class ValueClassNeedsOneValParam(valueClass: Symbol)(using Context) extends SyntaxMsg(ValueClassNeedsExactlyOneValParamID) { - def msg = e"""Value class needs one ${hl("val")} parameter""" - def explain = "" + def msg(using Context) = e"""Value class needs one ${hl("val")} parameter""" + def explain(using Context) = "" } class ValueClassParameterMayNotBeCallByName(valueClass: Symbol, param: Symbol)(using Context) extends SyntaxMsg(ValueClassParameterMayNotBeCallByNameID) { - def msg = s"Value class parameter `${param.name}` may not be call-by-name" - def explain = "" + def msg(using Context) = s"Value class parameter `${param.name}` may not be call-by-name" + def explain(using Context) = "" } class SuperCallsNotAllowedInlineable(symbol: Symbol)(using Context) extends SyntaxMsg(SuperCallsNotAllowedInlineableID) { - def msg = e"Super call not allowed in inlineable $symbol" - def explain = "Method inlining prohibits calling superclass methods, as it may lead to confusion about which super is being called." + def msg(using Context) = e"Super call not allowed in inlineable $symbol" + def explain(using Context) = "Method inlining prohibits calling superclass methods, as it may lead to confusion about which super is being called." } class NotAPath(tp: Type, usage: String)(using Context) extends TypeMsg(NotAPathID): - def msg = e"$tp is not a valid $usage, since it is not an immutable path" - def explain = + def msg(using Context) = e"$tp is not a valid $usage, since it is not an immutable path" + def explain(using Context) = i"""An immutable path is | - a reference to an immutable value, or | - a reference to `this`, or @@ -1679,28 +1679,28 @@ class NotAPath(tp: Type, usage: String)(using Context) extends TypeMsg(NotAPathI class WrongNumberOfParameters(expected: Int)(using Context) extends SyntaxMsg(WrongNumberOfParametersID) { - def msg = s"Wrong number of parameters, expected: $expected" - def explain = "" + def msg(using Context) = s"Wrong number of parameters, expected: $expected" + def explain(using Context) = "" } class DuplicatePrivateProtectedQualifier()(using Context) extends SyntaxMsg(DuplicatePrivateProtectedQualifierID) { - def msg = "Duplicate private/protected qualifier" - def explain = + def msg(using Context) = "Duplicate private/protected qualifier" + def explain(using Context) = e"It is not allowed to combine `private` and `protected` modifiers even if they are qualified to different scopes" } class ExpectedStartOfTopLevelDefinition()(using Context) extends SyntaxMsg(ExpectedStartOfTopLevelDefinitionID) { - def msg = "Expected start of definition" - def explain = + def msg(using Context) = "Expected start of definition" + def explain(using Context) = e"You have to provide either ${hl("class")}, ${hl("trait")}, ${hl("object")}, or ${hl("enum")} definitions after qualifiers" } class NoReturnFromInlineable(owner: Symbol)(using Context) extends SyntaxMsg(NoReturnFromInlineableID) { - def msg = e"No explicit ${hl("return")} allowed from inlineable $owner" - def explain = + def msg(using Context) = e"No explicit ${hl("return")} allowed from inlineable $owner" + def explain(using Context) = e"""Methods marked with ${hl("inline")} modifier may not use ${hl("return")} statements. |Instead, you should rely on the last expression's value being |returned from a method. @@ -1709,8 +1709,8 @@ class NoReturnFromInlineable(owner: Symbol)(using Context) class ReturnOutsideMethodDefinition(owner: Symbol)(using Context) extends SyntaxMsg(ReturnOutsideMethodDefinitionID) { - def msg = e"${hl("return")} outside method definition" - def explain = + def msg(using Context) = e"${hl("return")} outside method definition" + def explain(using Context) = e"""You used ${hl("return")} in ${owner}. |${hl("return")} is a keyword and may only be used within method declarations. |""" @@ -1718,16 +1718,16 @@ class ReturnOutsideMethodDefinition(owner: Symbol)(using Context) class ExtendFinalClass(clazz:Symbol, finalClazz: Symbol)(using Context) extends SyntaxMsg(ExtendFinalClassID) { - def msg = e"$clazz cannot extend ${hl("final")} $finalClazz" - def explain = + def msg(using Context) = e"$clazz cannot extend ${hl("final")} $finalClazz" + def explain(using Context) = e"""A class marked with the ${hl("final")} keyword cannot be extended""" } class ExpectedTypeBoundOrEquals(found: Token)(using Context) extends SyntaxMsg(ExpectedTypeBoundOrEqualsID) { - def msg = e"${hl("=")}, ${hl(">:")}, or ${hl("<:")} expected, but ${Tokens.showToken(found)} found" + def msg(using Context) = e"${hl("=")}, ${hl(">:")}, or ${hl("<:")} expected, but ${Tokens.showToken(found)} found" - def explain = + def explain(using Context) = e"""Type parameters and abstract types may be constrained by a type bound. |Such type bounds limit the concrete values of the type variables and possibly |reveal more information about the members of such types. @@ -1742,10 +1742,10 @@ class ExpectedTypeBoundOrEquals(found: Token)(using Context) class ClassAndCompanionNameClash(cls: Symbol, other: Symbol)(using Context) extends NamingMsg(ClassAndCompanionNameClashID) { - def msg = + def msg(using Context) = val name = cls.name.stripModuleClassSuffix e"Name clash: both ${cls.owner} and its companion object defines $name" - def explain = + def explain(using Context) = e"""|A ${cls.kindString} and its companion object cannot both define a ${hl("class")}, ${hl("trait")} or ${hl("object")} with the same name: | - ${cls.owner} defines ${cls} | - ${other.owner} defines ${other}""" @@ -1753,7 +1753,7 @@ class ClassAndCompanionNameClash(cls: Symbol, other: Symbol)(using Context) class TailrecNotApplicable(symbol: Symbol)(using Context) extends SyntaxMsg(TailrecNotApplicableID) { - def msg = { + def msg(using Context) = { val reason = if (!symbol.is(Method)) e"$symbol isn't a method" else if (symbol.is(Deferred)) e"$symbol is abstract" @@ -1762,13 +1762,13 @@ class TailrecNotApplicable(symbol: Symbol)(using Context) s"TailRec optimisation not applicable, $reason" } - def explain = "" + def explain(using Context) = "" } class FailureToEliminateExistential(tp: Type, tp1: Type, tp2: Type, boundSyms: List[Symbol], classRoot: Symbol)(using Context) extends Message(FailureToEliminateExistentialID) { def kind = MessageKind.Compatibility - def msg = + def msg(using Context) = val originalType = ctx.printer.dclsText(boundSyms, "; ").show e"""An existential type that came from a Scala-2 classfile for $classRoot |cannot be mapped accurately to a Scala-3 equivalent. @@ -1777,7 +1777,7 @@ class FailureToEliminateExistential(tp: Type, tp1: Type, tp2: Type, boundSyms: L |type used instead: $tp2 |This choice can cause follow-on type errors or hide type errors. |Proceed at own risk.""" - def explain = + def explain(using Context) = e"""Existential types in their full generality are no longer supported. |Scala-3 does applications of class types to wildcard type arguments. |Other forms of existential types that come from Scala-2 classfiles @@ -1786,16 +1786,16 @@ class FailureToEliminateExistential(tp: Type, tp1: Type, tp2: Type, boundSyms: L class OnlyFunctionsCanBeFollowedByUnderscore(tp: Type)(using Context) extends SyntaxMsg(OnlyFunctionsCanBeFollowedByUnderscoreID) { - def msg = e"Only function types can be followed by ${hl("_")} but the current expression has type $tp" - def explain = + def msg(using Context) = e"Only function types can be followed by ${hl("_")} but the current expression has type $tp" + def explain(using Context) = e"""The syntax ${hl("x _")} is no longer supported if ${hl("x")} is not a function. |To convert to a function value, you need to explicitly write ${hl("() => x")}""" } class MissingEmptyArgumentList(method: String)(using Context) extends SyntaxMsg(MissingEmptyArgumentListID) { - def msg = e"$method must be called with ${hl("()")} argument" - def explain = { + def msg(using Context) = e"$method must be called with ${hl("()")} argument" + def explain(using Context) = { val codeExample = """def next(): T = ... |next // is expanded to next()""" @@ -1811,31 +1811,31 @@ class MissingEmptyArgumentList(method: String)(using Context) class DuplicateNamedTypeParameter(name: Name)(using Context) extends SyntaxMsg(DuplicateNamedTypeParameterID) { - def msg = e"Type parameter $name was defined multiple times." - def explain = "" + def msg(using Context) = e"Type parameter $name was defined multiple times." + def explain(using Context) = "" } class UndefinedNamedTypeParameter(undefinedName: Name, definedNames: List[Name])(using Context) extends SyntaxMsg(UndefinedNamedTypeParameterID) { - def msg = e"Type parameter $undefinedName is undefined. Expected one of ${definedNames.map(_.show).mkString(", ")}." - def explain = "" + def msg(using Context) = e"Type parameter $undefinedName is undefined. Expected one of ${definedNames.map(_.show).mkString(", ")}." + def explain(using Context) = "" } class IllegalStartOfStatement(what: String, isModifier: Boolean, isStat: Boolean)(using Context) extends SyntaxMsg(IllegalStartOfStatementID) { - def msg = + def msg(using Context) = if isStat then "this kind of statement is not allowed here" else val addendum = if isModifier then ": this modifier is not allowed here" else "" s"Illegal start of $what$addendum" - def explain = + def explain(using Context) = i"""A statement is an import or export, a definition or an expression. |Some statements are only allowed in certain contexts""" } class TraitIsExpected(symbol: Symbol)(using Context) extends SyntaxMsg(TraitIsExpectedID) { - def msg = e"$symbol is not a trait" - def explain = { + def msg(using Context) = e"$symbol is not a trait" + def explain(using Context) = { val errorCodeExample = """class A |class B @@ -1861,12 +1861,13 @@ class TraitIsExpected(symbol: Symbol)(using Context) extends SyntaxMsg(TraitIsEx } class TraitRedefinedFinalMethodFromAnyRef(method: Symbol)(using Context) extends SyntaxMsg(TraitRedefinedFinalMethodFromAnyRefID) { - def msg = e"Traits cannot redefine final $method from ${hl("class AnyRef")}." - def explain = "" + def msg(using Context) = e"Traits cannot redefine final $method from ${hl("class AnyRef")}." + def explain(using Context) = "" } -class AlreadyDefined(name: Name, owner: Symbol, conflicting: Symbol)(using Context) extends NamingMsg(AlreadyDefinedID): - def msg = +class AlreadyDefined(name: Name, owner: Symbol, conflicting: Symbol)(using Context) +extends NamingMsg(AlreadyDefinedID): + def msg(using Context) = def where: String = if conflicting.effectiveOwner.is(Package) && conflicting.associatedFile != null then i" in ${conflicting.associatedFile}" @@ -1880,14 +1881,14 @@ class AlreadyDefined(name: Name, owner: Symbol, conflicting: Symbol)(using Conte e"$name clashes with $conflicting$where; the two must be defined together" else e"$name is already defined as $conflicting$where$note" - def explain = "" + def explain(using Context) = "" class PackageNameAlreadyDefined(pkg: Symbol)(using Context) extends NamingMsg(PackageNameAlreadyDefinedID) { - def msg = + def msg(using Context) = def where = if pkg.associatedFile == null then "" else s" in ${pkg.associatedFile}" e"""${pkg.name} is the name of $pkg$where. |It cannot be used at the same time as the name of a package.""" - def explain = + def explain(using Context) = def or = if pkg.associatedFile == null then "" else " or delete the containing class file" e"""An ${hl("object")} or other toplevel definition cannot have the same name as an existing ${hl("package")}. |Rename either one of them$or.""" @@ -1895,8 +1896,8 @@ class PackageNameAlreadyDefined(pkg: Symbol)(using Context) extends NamingMsg(Pa class UnapplyInvalidNumberOfArguments(qual: untpd.Tree, argTypes: List[Type])(using Context) extends SyntaxMsg(UnapplyInvalidNumberOfArgumentsID) { - def msg = e"Wrong number of argument patterns for $qual; expected: ($argTypes%, %)" - def explain = + def msg(using Context) = e"Wrong number of argument patterns for $qual; expected: ($argTypes%, %)" + def explain(using Context) = e"""The Unapply method of $qual was used with incorrect number of arguments. |Expected usage would be something like: |case $qual(${argTypes.map(_ => '_')}%, %) => ... @@ -1907,13 +1908,13 @@ class UnapplyInvalidNumberOfArguments(qual: untpd.Tree, argTypes: List[Type])(us class UnapplyInvalidReturnType(unapplyResult: Type, unapplyName: Name)(using Context) extends DeclarationMsg(UnapplyInvalidReturnTypeID) { - def msg = + def msg(using Context) = val addendum = if Feature.migrateTo3 && unapplyName == nme.unapplySeq then "\nYou might want to try to rewrite the extractor to use `unapply` instead." else "" e"""| ${Red(i"$unapplyResult")} is not a valid result type of an $unapplyName method of an ${Magenta("extractor")}.$addendum""" - def explain = if (unapplyName.show == "unapply") + def explain(using Context) = if (unapplyName.show == "unapply") e""" |To be used as an extractor, an unapply method has to return a type that either: | - has members ${Magenta("isEmpty: Boolean")} and ${Magenta("get: S")} (usually an ${Green("Option[S]")}) @@ -1961,14 +1962,14 @@ class UnapplyInvalidReturnType(unapplyResult: Type, unapplyName: Name)(using Con } class StaticFieldsOnlyAllowedInObjects(member: Symbol)(using Context) extends SyntaxMsg(StaticFieldsOnlyAllowedInObjectsID) { - def msg = e"${hl("@static")} $member in ${member.owner} must be defined inside a static ${hl("object")}." - def explain = + def msg(using Context) = e"${hl("@static")} $member in ${member.owner} must be defined inside a static ${hl("object")}." + def explain(using Context) = e"${hl("@static")} members are only allowed inside objects." } class StaticFieldsShouldPrecedeNonStatic(member: Symbol, defns: List[tpd.Tree])(using Context) extends SyntaxMsg(StaticFieldsShouldPrecedeNonStaticID) { - def msg = e"${hl("@static")} $member in ${member.owner} must be defined before non-static fields." - def explain = { + def msg(using Context) = e"${hl("@static")} $member in ${member.owner} must be defined before non-static fields." + def explain(using Context) = { val nonStatics = defns.takeWhile(_.symbol != member).take(3).filter(_.isInstanceOf[tpd.ValDef]) val codeExample = s"""object ${member.owner.name.firstPart} { | @static ${member} = ... @@ -1987,8 +1988,8 @@ class StaticFieldsShouldPrecedeNonStatic(member: Symbol, defns: List[tpd.Tree])( } class CyclicInheritance(symbol: Symbol, addendum: => String)(using Context) extends SyntaxMsg(CyclicInheritanceID) { - def msg = e"Cyclic inheritance: $symbol extends itself$addendum" - def explain = { + def msg(using Context) = e"Cyclic inheritance: $symbol extends itself$addendum" + def explain(using Context) = { val codeExample = "class A extends A" e"""Cyclic inheritance is prohibited in Dotty. @@ -2004,7 +2005,7 @@ class CyclicInheritance(symbol: Symbol, addendum: => String)(using Context) exte class BadSymbolicReference(denot: SymDenotation)(using Context) extends ReferenceMsg(BadSymbolicReferenceID) { - def msg = { + def msg(using Context) = { val denotationOwner = denot.owner val denotationName = ctx.fresh.setSetting(ctx.settings.YdebugNames, true).printer.nameString(denot.name) val file = denot.symbol.associatedFile @@ -2018,18 +2019,18 @@ extends ReferenceMsg(BadSymbolicReferenceID) { |the classpath might be incompatible with the version used when compiling $src.""" } - def explain = "" + def explain(using Context) = "" } class UnableToExtendSealedClass(pclazz: Symbol)(using Context) extends SyntaxMsg(UnableToExtendSealedClassID) { - def msg = e"Cannot extend ${hl("sealed")} $pclazz in a different source file" - def explain = "A sealed class or trait can only be extended in the same file as its declaration" + def msg(using Context) = e"Cannot extend ${hl("sealed")} $pclazz in a different source file" + def explain(using Context) = "A sealed class or trait can only be extended in the same file as its declaration" } class SymbolHasUnparsableVersionNumber(symbol: Symbol, errorMessage: String)(using Context) extends SyntaxMsg(SymbolHasUnparsableVersionNumberID) { - def msg = e"${symbol.showLocated} has an unparsable version number: $errorMessage" - def explain = + def msg(using Context) = e"${symbol.showLocated} has an unparsable version number: $errorMessage" + def explain(using Context) = e"""The ${symbol.showLocated} is marked with ${hl("@migration")} indicating it has changed semantics |between versions and the ${hl("-Xmigration")} settings is used to warn about constructs |whose behavior may have changed since version change.""" @@ -2040,8 +2041,8 @@ class SymbolChangedSemanticsInVersion( migrationVersion: ScalaVersion, migrationMessage: String )(using Context) extends SyntaxMsg(SymbolChangedSemanticsInVersionID) { - def msg = e"${symbol.showLocated} has changed semantics in version $migrationVersion: $migrationMessage" - def explain = + def msg(using Context) = e"${symbol.showLocated} has changed semantics in version $migrationVersion: $migrationMessage" + def explain(using Context) = e"""The ${symbol.showLocated} is marked with ${hl("@migration")} indicating it has changed semantics |between versions and the ${hl("-Xmigration")} settings is used to warn about constructs |whose behavior may have changed since version change.""" @@ -2049,8 +2050,8 @@ class SymbolChangedSemanticsInVersion( class UnableToEmitSwitch()(using Context) extends SyntaxMsg(UnableToEmitSwitchID) { - def msg = e"Could not emit switch for ${hl("@switch")} annotated match" - def explain = { + def msg(using Context) = e"Could not emit switch for ${hl("@switch")} annotated match" + def explain(using Context) = { val codeExample = """val ConstantB = 'B' |final val ConstantC = 'C' @@ -2077,15 +2078,15 @@ extends SyntaxMsg(UnableToEmitSwitchID) { class MissingCompanionForStatic(member: Symbol)(using Context) extends SyntaxMsg(MissingCompanionForStaticID) { - def msg = e"${member.owner} does not have a companion class" - def explain = + def msg(using Context) = e"${member.owner} does not have a companion class" + def explain(using Context) = e"An object that contains ${hl("@static")} members must have a companion class." } class PolymorphicMethodMissingTypeInParent(rsym: Symbol, parentSym: Symbol)(using Context) extends SyntaxMsg(PolymorphicMethodMissingTypeInParentID) { - def msg = e"Polymorphic refinement $rsym without matching type in parent $parentSym is no longer allowed" - def explain = + def msg(using Context) = e"Polymorphic refinement $rsym without matching type in parent $parentSym is no longer allowed" + def explain(using Context) = e"""Polymorphic $rsym is not allowed in the structural refinement of $parentSym because |$rsym does not override any method in $parentSym. Structural refinement does not allow for |polymorphic methods.""" @@ -2093,23 +2094,24 @@ extends SyntaxMsg(PolymorphicMethodMissingTypeInParentID) { class ParamsNoInline(owner: Symbol)(using Context) extends SyntaxMsg(ParamsNoInlineID) { - def msg = e"""${hl("inline")} modifier can only be used for parameters of inline methods""" - def explain = "" + def msg(using Context) = e"""${hl("inline")} modifier can only be used for parameters of inline methods""" + def explain(using Context) = "" } class JavaSymbolIsNotAValue(symbol: Symbol)(using Context) extends TypeMsg(JavaSymbolIsNotAValueID) { - def msg = { + def msg(using Context) = { val kind = if (symbol is Package) e"$symbol" else e"Java defined ${hl("class " + symbol.name)}" s"$kind is not a value" } - def explain = "" + def explain(using Context) = "" } -class DoubleDefinition(decl: Symbol, previousDecl: Symbol, base: Symbol)(using Context) extends NamingMsg(DoubleDefinitionID) { - def msg = { +class DoubleDefinition(decl: Symbol, previousDecl: Symbol, base: Symbol)(using Context) +extends NamingMsg(DoubleDefinitionID) { + def msg(using Context) = { def nameAnd = if (decl.name != previousDecl.name) " name and" else "" def erasedType = if ctx.erasedTypes then i" ${decl.info}" else "" def details(using Context): String = @@ -2165,38 +2167,38 @@ class DoubleDefinition(decl: Symbol, previousDecl: Symbol, base: Symbol)(using C |""" } + details } - def explain = "" + def explain(using Context) = "" } class ImportRenamedTwice(ident: untpd.Ident)(using Context) extends SyntaxMsg(ImportRenamedTwiceID) { - def msg = s"${ident.show} is renamed twice on the same import line." - def explain = "" + def msg(using Context) = s"${ident.show} is renamed twice on the same import line." + def explain(using Context) = "" } class TypeTestAlwaysDiverges(scrutTp: Type, testTp: Type)(using Context) extends SyntaxMsg(TypeTestAlwaysDivergesID) { - def msg = + def msg(using Context) = s"This type test will never return a result since the scrutinee type ${scrutTp.show} does not contain any value." - def explain = "" + def explain(using Context) = "" } // Relative of CyclicReferenceInvolvingImplicit and RecursiveValueNeedsResultType class TermMemberNeedsResultTypeForImplicitSearch(cycleSym: Symbol)(using Context) extends CyclicMsg(TermMemberNeedsNeedsResultTypeForImplicitSearchID) { - def msg = e"""$cycleSym needs result type because its right-hand side attempts implicit search""" - def explain = + def msg(using Context) = e"""$cycleSym needs result type because its right-hand side attempts implicit search""" + def explain(using Context) = e"""|The right hand-side of $cycleSym's definition requires an implicit search at the highlighted position. |To avoid this error, give `$cycleSym` an explicit type. |""" } class ClassCannotExtendEnum(cls: Symbol, parent: Symbol)(using Context) extends SyntaxMsg(ClassCannotExtendEnumID) { - def msg = e"""$cls in ${cls.owner} extends enum ${parent.name}, but extending enums is prohibited.""" - def explain = "" + def msg(using Context) = e"""$cls in ${cls.owner} extends enum ${parent.name}, but extending enums is prohibited.""" + def explain(using Context) = "" } class NotAnExtractor(tree: untpd.Tree)(using Context) extends SyntaxMsg(NotAnExtractorID) { - def msg = e"$tree cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method" - def explain = + def msg(using Context) = e"$tree cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method" + def explain(using Context) = e"""|An ${hl("unapply")} method should be defined in an ${hl("object")} as follow: | - If it is just a test, return a ${hl("Boolean")}. For example ${hl("case even()")} | - If it returns a single sub-value of type T, return an ${hl("Option[T]")} @@ -2209,63 +2211,63 @@ class NotAnExtractor(tree: untpd.Tree)(using Context) extends SyntaxMsg(NotAnExt class MemberWithSameNameAsStatic()(using Context) extends SyntaxMsg(MemberWithSameNameAsStaticID) { - def msg = e"Companion classes cannot define members with same name as a ${hl("@static")} member" - def explain = "" + def msg(using Context) = e"Companion classes cannot define members with same name as a ${hl("@static")} member" + def explain(using Context) = "" } class PureExpressionInStatementPosition(stat: untpd.Tree, val exprOwner: Symbol)(using Context) extends Message(PureExpressionInStatementPositionID) { def kind = MessageKind.PotentialIssue - def msg = "A pure expression does nothing in statement position; you may be omitting necessary parentheses" - def explain = + def msg(using Context) = "A pure expression does nothing in statement position; you may be omitting necessary parentheses" + def explain(using Context) = e"""The pure expression $stat doesn't have any side effect and its result is not assigned elsewhere. |It can be removed without changing the semantics of the program. This may indicate an error.""" } class TraitCompanionWithMutableStatic()(using Context) extends SyntaxMsg(TraitCompanionWithMutableStaticID) { - def msg = e"Companion of traits cannot define mutable @static fields" - def explain = "" + def msg(using Context) = e"Companion of traits cannot define mutable @static fields" + def explain(using Context) = "" } class LazyStaticField()(using Context) extends SyntaxMsg(LazyStaticFieldID) { - def msg = e"Lazy @static fields are not supported" - def explain = "" + def msg(using Context) = e"Lazy @static fields are not supported" + def explain(using Context) = "" } class StaticOverridingNonStaticMembers()(using Context) extends SyntaxMsg(StaticOverridingNonStaticMembersID) { - def msg = e"${hl("@static")} members cannot override or implement non-static ones" - def explain = "" + def msg(using Context) = e"${hl("@static")} members cannot override or implement non-static ones" + def explain(using Context) = "" } class OverloadInRefinement(rsym: Symbol)(using Context) extends DeclarationMsg(OverloadInRefinementID) { - def msg = "Refinements cannot introduce overloaded definitions" - def explain = + def msg(using Context) = "Refinements cannot introduce overloaded definitions" + def explain(using Context) = e"""The refinement `$rsym` introduces an overloaded definition. |Refinements cannot contain overloaded definitions.""" } class NoMatchingOverload(val alternatives: List[SingleDenotation], pt: Type)(using Context) extends TypeMsg(NoMatchingOverloadID) { - def msg = + def msg(using Context) = e"""None of the ${err.overloadedAltsStr(alternatives)} |match ${err.expectedTypeStr(pt)}""" - def explain = "" + def explain(using Context) = "" } class StableIdentPattern(tree: untpd.Tree, pt: Type)(using Context) extends TypeMsg(StableIdentPatternID) { - def msg = + def msg(using Context) = e"""Stable identifier required, but $tree found""" - def explain = "" + def explain(using Context) = "" } class IllegalSuperAccessor(base: Symbol, memberName: Name, targetName: Name, acc: Symbol, accTp: Type, other: Symbol, otherTp: Type)(using Context) extends DeclarationMsg(IllegalSuperAccessorID) { - def msg = { + def msg(using Context) = { // The mixin containing a super-call that requires a super-accessor val accMixin = acc.owner // The class or trait that the super-accessor should resolve too in `base` @@ -2315,14 +2317,14 @@ class IllegalSuperAccessor(base: Symbol, memberName: Name, targetName: Name, | super-call to a specific parent, e.g. $staticSuperCall |""" } - def explain = "" + def explain(using Context) = "" } class TraitParameterUsedAsParentPrefix(cls: Symbol)(using Context) extends DeclarationMsg(TraitParameterUsedAsParentPrefixID) { - def msg = + def msg(using Context) = s"${cls.show} cannot extend from a parent that is derived via its own parameters" - def explain = + def explain(using Context) = ex""" |The parent class/trait that ${cls.show} extends from is obtained from |the parameter of ${cls.show}. This is disallowed in order to prevent @@ -2335,9 +2337,9 @@ class TraitParameterUsedAsParentPrefix(cls: Symbol)(using Context) class UnknownNamedEnclosingClassOrObject(name: TypeName)(using Context) extends ReferenceMsg(UnknownNamedEnclosingClassOrObjectID) { - def msg = + def msg(using Context) = e"""no enclosing class or object is named '${hl(name.show)}'""" - def explain = + def explain(using Context) = ex""" |The class or object named '${hl(name.show)}' was used as a visibility |modifier, but could not be resolved. Make sure that @@ -2348,26 +2350,26 @@ class UnknownNamedEnclosingClassOrObject(name: TypeName)(using Context) class IllegalCyclicTypeReference(sym: Symbol, where: String, lastChecked: Type)(using Context) extends CyclicMsg(IllegalCyclicTypeReferenceID) { - def msg = + def msg(using Context) = val lastCheckedStr = try lastChecked.show catch case ex: CyclicReference => "..." i"illegal cyclic type reference: ${where} ${hl(lastCheckedStr)} of $sym refers back to the type itself" - def explain = "" + def explain(using Context) = "" } class ErasedTypesCanOnlyBeFunctionTypes()(using Context) extends SyntaxMsg(ErasedTypesCanOnlyBeFunctionTypesID) { - def msg = "Types with erased keyword can only be function types `(erased ...) => ...`" - def explain = "" + def msg(using Context) = "Types with erased keyword can only be function types `(erased ...) => ...`" + def explain(using Context) = "" } class CaseClassMissingNonImplicitParamList(cdef: untpd.TypeDef)(using Context) extends SyntaxMsg(CaseClassMissingNonImplicitParamListID) { - def msg = + def msg(using Context) = e"""|A ${hl("case class")} must have at least one leading non-implicit parameter list""" - def explain = + def explain(using Context) = e"""|${cdef.name} must have at least one leading non-implicit parameter list, | if you're aiming to have a case class parametrized only by implicit ones, you should | add an explicit ${hl("()")} as the first parameter list to ${cdef.name}.""" @@ -2375,9 +2377,9 @@ class CaseClassMissingNonImplicitParamList(cdef: untpd.TypeDef)(using Context) class EnumerationsShouldNotBeEmpty(cdef: untpd.TypeDef)(using Context) extends SyntaxMsg(EnumerationsShouldNotBeEmptyID) { - def msg = "Enumerations must contain at least one case" + def msg(using Context) = "Enumerations must contain at least one case" - def explain = + def explain(using Context) = e"""|Enumeration ${cdef.name} must contain at least one case |Example Usage: | ${hl("enum")} ${cdef.name} { @@ -2388,9 +2390,9 @@ class EnumerationsShouldNotBeEmpty(cdef: untpd.TypeDef)(using Context) class TypedCaseDoesNotExplicitlyExtendTypedEnum(enumDef: Symbol, caseDef: untpd.TypeDef)(using Context) extends SyntaxMsg(TypedCaseDoesNotExplicitlyExtendTypedEnumID) { - def msg = i"explicit extends clause needed because both enum case and enum class have type parameters" + def msg(using Context) = i"explicit extends clause needed because both enum case and enum class have type parameters" - def explain = + def explain(using Context) = e"""Enumerations where the enum class as well as the enum case have type parameters need |an explicit extends. |for example: @@ -2402,8 +2404,8 @@ class TypedCaseDoesNotExplicitlyExtendTypedEnum(enumDef: Symbol, caseDef: untpd. class IllegalRedefinitionOfStandardKind(kindType: String, name: Name)(using Context) extends SyntaxMsg(IllegalRedefinitionOfStandardKindID) { - def msg = e"illegal redefinition of standard $kindType $name" - def explain = + def msg(using Context) = e"illegal redefinition of standard $kindType $name" + def explain(using Context) = e"""| "$name" is a standard Scala core `$kindType` | Please choose a different name to avoid conflicts |""" @@ -2411,8 +2413,8 @@ class IllegalRedefinitionOfStandardKind(kindType: String, name: Name)(using Cont class NoExtensionMethodAllowed(mdef: untpd.DefDef)(using Context) extends SyntaxMsg(NoExtensionMethodAllowedID) { - def msg = e"No extension method allowed here, since collective parameters are given" - def explain = + def msg(using Context) = e"No extension method allowed here, since collective parameters are given" + def explain(using Context) = e"""|Extension method: | `${mdef}` |is defined inside an extension clause which has collective parameters. @@ -2421,9 +2423,9 @@ class NoExtensionMethodAllowed(mdef: untpd.DefDef)(using Context) class ExtensionMethodCannotHaveTypeParams(mdef: untpd.DefDef)(using Context) extends SyntaxMsg(ExtensionMethodCannotHaveTypeParamsID) { - def msg = i"Extension method cannot have type parameters since some were already given previously" + def msg(using Context) = i"Extension method cannot have type parameters since some were already given previously" - def explain = + def explain(using Context) = e"""|Extension method: | `${mdef}` |has type parameters `[${mdef.leadingTypeParams.map(_.show).mkString(",")}]`, while the extension clause has @@ -2433,8 +2435,8 @@ class ExtensionMethodCannotHaveTypeParams(mdef: untpd.DefDef)(using Context) class ExtensionCanOnlyHaveDefs(mdef: untpd.Tree)(using Context) extends SyntaxMsg(ExtensionCanOnlyHaveDefsID) { - def msg = e"Only methods allowed here, since collective parameters are given" - def explain = + def msg(using Context) = e"Only methods allowed here, since collective parameters are given" + def explain(using Context) = e"""Extension clauses can only have `def`s | `${mdef.show}` is not a valid expression here. |""" @@ -2442,8 +2444,8 @@ class ExtensionCanOnlyHaveDefs(mdef: untpd.Tree)(using Context) class UnexpectedPatternForSummonFrom(tree: Tree[_])(using Context) extends SyntaxMsg(UnexpectedPatternForSummonFromID) { - def msg = e"Unexpected pattern for summonFrom. Expected ${hl("`x: T`")} or ${hl("`_`")}" - def explain = + def msg(using Context) = e"Unexpected pattern for summonFrom. Expected ${hl("`x: T`")} or ${hl("`_`")}" + def explain(using Context) = e"""|The pattern "${tree.show}" provided in the ${hl("case")} expression of the ${hl("summonFrom")}, | needs to be of the form ${hl("`x: T`")} or ${hl("`_`")}. | @@ -2461,8 +2463,8 @@ class UnexpectedPatternForSummonFrom(tree: Tree[_])(using Context) class AnonymousInstanceCannotBeEmpty(impl: untpd.Template)(using Context) extends SyntaxMsg(AnonymousInstanceCannotBeEmptyID) { - def msg = i"anonymous instance must implement a type or have at least one extension method" - def explain = + def msg(using Context) = i"anonymous instance must implement a type or have at least one extension method" + def explain(using Context) = e"""|Anonymous instances cannot be defined with an empty body. The block |`${impl.show}` should either contain an implemented type or at least one extension method. |""" @@ -2470,30 +2472,30 @@ class AnonymousInstanceCannotBeEmpty(impl: untpd.Template)(using Context) class ModifierNotAllowedForDefinition(flag: Flag)(using Context) extends SyntaxMsg(ModifierNotAllowedForDefinitionID) { - def msg = e"Modifier ${hl(flag.flagsString)} is not allowed for this definition" - def explain = "" + def msg(using Context) = e"Modifier ${hl(flag.flagsString)} is not allowed for this definition" + def explain(using Context) = "" } class RedundantModifier(flag: Flag)(using Context) extends SyntaxMsg(RedundantModifierID) { - def msg = e"Modifier ${hl(flag.flagsString)} is redundant for this definition" - def explain = "" + def msg(using Context) = e"Modifier ${hl(flag.flagsString)} is redundant for this definition" + def explain(using Context) = "" } class InvalidReferenceInImplicitNotFoundAnnotation(typeVar: String, owner: String)(using Context) extends ReferenceMsg(InvalidReferenceInImplicitNotFoundAnnotationID) { - def msg = e"""|Invalid reference to a type variable ${hl(typeVar)} found in the annotation argument. + def msg(using Context) = e"""|Invalid reference to a type variable ${hl(typeVar)} found in the annotation argument. |The variable does not occur as a parameter in the scope of ${hl(owner)}. |""" - def explain = "" + def explain(using Context) = "" } class CaseClassInInlinedCode(tree: tpd.Tree)(using Context) extends SyntaxMsg(CaseClassInInlinedCodeID) { def defKind = if tree.symbol.is(Module) then "object" else "class" - def msg = s"Case $defKind definitions are not allowed in inline methods or quoted code. Use a normal $defKind instead." - def explain = + def msg(using Context) = s"Case $defKind definitions are not allowed in inline methods or quoted code. Use a normal $defKind instead." + def explain(using Context) = e"""Case class/object definitions generate a considerable footprint in code size. |Inlining such definition would multiply this footprint for each call site. |""" @@ -2504,7 +2506,7 @@ class ImplicitSearchTooLargeWarning(limit: Int, openSearchPairs: List[(Candidate override def showAlways = true def showQuery(query: (Candidate, Type))(using Context): String = i" ${query._1.ref.symbol.showLocated} for ${query._2}}" - def msg = + def msg(using Context) = e"""Implicit search problem too large. |an implicit search was terminated with failure after trying $limit expressions. |The root candidate for the search was: @@ -2515,7 +2517,7 @@ class ImplicitSearchTooLargeWarning(limit: Int, openSearchPairs: List[(Candidate |Smaller values cause the search to fail faster. |Larger values might make a very large search problem succeed. |""" - def explain = + def explain(using Context) = e"""The overflow happened with the following lists of tried expressions and target types, |starting with the root query: | @@ -2524,8 +2526,8 @@ class ImplicitSearchTooLargeWarning(limit: Int, openSearchPairs: List[(Candidate class TargetNameOnTopLevelClass(symbol: Symbol)(using Context) extends SyntaxMsg(TargetNameOnTopLevelClassID): - def msg = e"${hl("@targetName")} annotation not allowed on top-level $symbol" - def explain = + def msg(using Context) = e"${hl("@targetName")} annotation not allowed on top-level $symbol" + def explain(using Context) = val annot = symbol.getAnnotation(defn.TargetNameAnnot).get e"""The @targetName annotation may be applied to a top-level ${hl("val")} or ${hl("def")}, but not |a top-level ${hl("class")}, ${hl("trait")}, or ${hl("object")}. @@ -2545,19 +2547,22 @@ extends SyntaxMsg(TargetNameOnTopLevelClassID): class NotClassType(tp: Type)(using Context) extends TypeMsg(NotClassTypeID), ShowMatchTrace(tp): - def msg = ex"$tp is not a class type" - def explain = "" + def msg(using Context) = ex"$tp is not a class type" + def explain(using Context) = "" class MissingImplicitArgument( arg: tpd.Tree, pt: Type, where: String, paramSymWithMethodCallTree: Option[(Symbol, tpd.Tree)] = None, - ignoredInstanceNormalImport: => Option[SearchSuccess], - importSuggestionAddendum: Type => String - )(using ctx: Context) extends TypeMsg(MissingImplicitArgumentID): + ignoredInstanceNormalImport: => Option[SearchSuccess] + )(using Context) extends TypeMsg(MissingImplicitArgumentID), ShowMatchTrace(pt): + + arg.tpe match + case ambi: AmbiguousImplicits => withoutDisambiguation() + case _ => - def msg: String = + def msg(using Context): String = def formatMsg(shortForm: String)(headline: String = shortForm) = arg match case arg: Trees.SearchFailureIdent[?] => @@ -2705,12 +2710,6 @@ class MissingImplicitArgument( val substituteType = (_: Type).asSeenFrom(pt, sym) formatAnnotationMessage(rawMsg, sym, substituteType) - def hiddenImplicitsAddendum: String = - def hiddenImplicitNote(s: SearchSuccess) = - i"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." - val normalImports = ignoredInstanceNormalImport.map(hiddenImplicitNote) - normalImports.getOrElse(importSuggestionAddendum(pt)) - object AmbiguousImplicitMsg { def unapply(search: SearchSuccess): Option[String] = userDefinedMsg(search.ref.symbol, defn.ImplicitAmbiguousAnnot) @@ -2733,15 +2732,28 @@ class MissingImplicitArgument( .orElse(userDefinedImplicitNotFoundTypeMessage) .getOrElse(defaultImplicitNotFoundMessage) formatMsg(shortMessage)() - ++ hiddenImplicitsAddendum - ++ matchReductionAddendum(pt) end msg - def explain = "" + + override def msgPostscript(using Context) = + arg.tpe match + case _: AmbiguousImplicits => + "" // show no disambiguation + case _: TooUnspecific => + super.msgPostscript // show just disambigutation and match type trace + case _ => + // show all available additional info + def hiddenImplicitNote(s: SearchSuccess) = + i"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." + super.msgPostscript + ++ ignoredInstanceNormalImport.map(hiddenImplicitNote) + .getOrElse(ctx.typer.importSuggestionAddendum(pt)) + + def explain(using Context) = "" end MissingImplicitArgument class CannotBeAccessed(tpe: NamedType, superAccess: Boolean)(using Context) extends ReferenceMsg(CannotBeAccessedID): - def msg = + def msg(using Context) = val pre = tpe.prefix val name = tpe.name val alts = tpe.denot.alternatives.map(_.symbol).filter(_.exists) @@ -2756,4 +2768,6 @@ extends ReferenceMsg(CannotBeAccessedID): val whyNot = new StringBuffer alts.foreach(_.isAccessibleFrom(pre, superAccess, whyNot)) e"$whatCanNot be accessed as a member of $pre$where.$whyNot" - def explain = "" + def explain(using Context) = "" + + diff --git a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala index 3b83a9bf1cd5..137275a144a5 100644 --- a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala @@ -247,8 +247,9 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( getQuoteTypeTags.getTagRef(tp) case _: SearchFailureType => report.error( - ctx.typer.missingArgMsg(tag, reqType, "").wrap( - i"Reference to $tp within quotes requires a given $reqType in scope.\n", "\n"), + ctx.typer.missingArgMsg(tag, reqType, "") + .prepend(i"Reference to $tp within quotes requires a given $reqType in scope.\n") + .append("\n"), pos) tp case _ => diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 54ab1a8e0501..cf7b08135c7b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -835,7 +835,7 @@ trait Checking { var reportedPt = pt.dropAnnot(defn.UncheckedAnnot) if !pat.tpe.isSingleton then reportedPt = reportedPt.widen val problem = if pat.tpe <:< reportedPt then "is more specialized than" else "does not match" - ex"pattern's type ${pat.tpe} $problem the right hand side expression's type $reportedPt" + em"pattern's type ${pat.tpe} $problem the right hand side expression's type $reportedPt" case RefutableExtractor => val extractor = val UnApply(fn, _, _) = pat: @unchecked @@ -843,9 +843,9 @@ trait Checking { case Select(id, _) => id case _ => EmptyTree if extractor.isEmpty then - i"pattern binding uses refutable extractor" + em"pattern binding uses refutable extractor" else - i"pattern binding uses refutable extractor `$extractor`" + em"pattern binding uses refutable extractor `$extractor`" val fix = if isPatDef then "adding `: @unchecked` after the expression" @@ -863,10 +863,11 @@ trait Checking { else pat.srcPos def rewriteMsg = Message.rewriteNotice("This patch", `3.2-migration`) report.gradualErrorOrMigrationWarning( - em"""$message - | - |If $usage is intentional, this can be communicated by $fix, - |which $addendum.$rewriteMsg""", + message.append( + i"""| + | + |If $usage is intentional, this can be communicated by $fix, + |which $addendum.$rewriteMsg"""), pos, warnFrom = `3.2`, errorFrom = `future`) false } diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 0f04dc60c06b..41d4bef5e81c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -444,9 +444,6 @@ object Implicits: else i"convert from ${argument.tpe} to ${clarify(expectedType)}" } - /** An explanation of the cause of the failure as a string */ - def explanation(using Context): String = msg.message - /** If search was for an implicit conversion, a note describing the failure * in more detail - this is either empty or starts with a '\n' */ @@ -510,7 +507,7 @@ object Implicits: |must be more specific than $target""" override def msg(using Context) = - super.msg.wrap("", "\nThe expected type $target is not specific enough, so no search was attempted") + super.msg.append("\nThe expected type $target is not specific enough, so no search was attempted") override def toString = s"TooUnspecific" /** An ambiguous implicits failure */ @@ -521,7 +518,7 @@ object Implicits: if str1 == str2 then str1 = ctx.printer.toTextRef(alt1.ref).show str2 = ctx.printer.toTextRef(alt2.ref).show - em"both $str1 and $str2 $qualify" + em"both $str1 and $str2 $qualify".withoutDisambiguation() override def whyNoConversion(using Context): String = if !argument.isEmpty && argument.tpe.widen.isRef(defn.NothingClass) then "" @@ -926,7 +923,7 @@ trait Implicits: // example where searching for a nested type causes an infinite loop. None - MissingImplicitArgument(arg, pt, where, paramSymWithMethodCallTree, ignoredInstanceNormalImport, importSuggestionAddendum) + MissingImplicitArgument(arg, pt, where, paramSymWithMethodCallTree, ignoredInstanceNormalImport) } /** A string indicating the formal parameter corresponding to a missing argument */ @@ -1047,9 +1044,8 @@ trait Implicits: withMode(Mode.OldOverloadingResolution)(inferImplicit(pt, argument, span)) match { case altResult: SearchSuccess => report.migrationWarning( - result.reason.msg.wrap( - s"According to new implicit resolution rules, this will be ambiguous:\n", - ""), + result.reason.msg + .prepend(s"According to new implicit resolution rules, this will be ambiguous:\n"), ctx.source.atSpan(span)) altResult case _ => diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 204c2217f3be..39d4f0868896 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1314,7 +1314,7 @@ class Namer { typer: Typer => case _ => 0 if cmp == 0 then report.error( - ex"""Clashing exports: The exported + em"""Clashing exports: The exported | ${forwarder.rhs.symbol}: ${alt1.widen} |and ${forwarder1.rhs.symbol}: ${alt2.widen} |have the same signature after erasure and overloading resolution could not disambiguate.""", diff --git a/compiler/test-resources/repl/i4184 b/compiler/test-resources/repl/i4184 index 1e57c3e5ad80..06b2c81ece21 100644 --- a/compiler/test-resources/repl/i4184 +++ b/compiler/test-resources/repl/i4184 @@ -8,5 +8,8 @@ scala> object Bar { new foo.Foo == new bar.Foo } -- [E172] Type Error: ---------------------------------------------------------- 1 | object Bar { new foo.Foo == new bar.Foo } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Values of types foo.Foo and bar.Foo cannot be compared with == or != -1 error found + | Values of types foo.Foo and bar.Foo² cannot be compared with == or != + | + | where: Foo is a class in object foo + | Foo² is a class in object bar +1 error found \ No newline at end of file diff --git a/compiler/test/dotty/tools/dotc/StringFormatterTest.scala b/compiler/test/dotty/tools/dotc/StringFormatterTest.scala index ec81011eecc8..4dfc08cc7e9b 100644 --- a/compiler/test/dotty/tools/dotc/StringFormatterTest.scala +++ b/compiler/test/dotty/tools/dotc/StringFormatterTest.scala @@ -39,51 +39,6 @@ class StringFormatterTest extends AbstractStringFormatterTest: assertEquals("flags=private final ", store.string) end StringFormatterTest -class EStringFormatterTest extends AbstractStringFormatterTest: - @Test def seq = check("[Any, String]", e"${Seq(defn.AnyType, defn.StringType)}") - @Test def seqSeq = check("Any; String", e"${Seq(defn.AnyType, defn.StringType)}%; %") - @Test def ellipsis = assert(e"$Big".contains("...")) - @Test def err = check("type Err", e"$Err") - @Test def ambig = check("Foo vs Foo", e"$Foo vs $Foo") - @Test def cstrd = check("Foo; Bar", e"$mkCstrd%; %") - @Test def seqErr = check("[class Any, type Err]", e"${Seq(defn.AnyClass, Err)}") - @Test def seqSeqErr = check("class Any; type Err", e"${Seq(defn.AnyClass, Err)}%; %") - @Test def tupleErr = check("(1,type Err)", e"${(1, Err)}") - @Test def tupleAmb = check("(Foo,Foo)", e"${(Foo, Foo)}") - @Test def tupleFlags = check("(Foo,abstract)", e"${(Foo, Abstract)}") - @Test def seqOfTupleFlags = check("[(Foo,abstract)]", e"${Seq((Foo, Abstract))}") -end EStringFormatterTest - -class ExStringFormatterTest extends AbstractStringFormatterTest: - @Test def seq = check("[Any, String]", ex"${Seq(defn.AnyType, defn.StringType)}") - @Test def seqSeq = check("Any; String", ex"${Seq(defn.AnyType, defn.StringType)}%; %") - @Test def ellipsis = assert(ex"$Big".contains("...")) - @Test def err = check("type Err", ex"$Err") - @Test def ambig = check("""Foo vs Foo² - | - |where: Foo is a type - | Foo² is a type - |""".stripMargin, ex"$Foo vs $Foo") - @Test def cstrd = check("""Foo; Bar - | - |where: Bar is a type variable with constraint <: String - | Foo is a type variable with constraint <: Int - |""".stripMargin, ex"$mkCstrd%; %") - @Test def seqErr = check("[class Any, type Err]", ex"${Seq(defn.AnyClass, Err)}") - @Test def seqSeqErr = check("class Any; type Err", ex"${Seq(defn.AnyClass, Err)}%; %") - @Test def tupleErr = check("(1,type Err)", ex"${(1, Err)}") - @Test def tupleAmb = check("""(Foo,Foo²) - | - |where: Foo is a type - | Foo² is a type - |""".stripMargin, ex"${(Foo, Foo)}") - @Test def seqOfTup3Amb = check("""[(Foo,Foo²,type Err)] - | - |where: Foo is a type - | Foo² is a type - |""".stripMargin, ex"${Seq((Foo, Foo, Err))}") -end ExStringFormatterTest - abstract class AbstractStringFormatterTest extends DottyTest: override def initializeCtx(fc: FreshContext) = super.initializeCtx(fc.setSetting(fc.settings.color, "never")) diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index b64142c0021f..62b1b88984bc 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -561,7 +561,7 @@ trait ParallelTesting extends RunnerOrchestration { self => def addToLast(str: String): Unit = diagnostics match case head :: tail => - diagnostics = Diagnostic.Error(s"${head.msg.rawMessage}$str", head.pos) :: tail + diagnostics = Diagnostic.Error(s"${head.msg.message}$str", head.pos) :: tail case Nil => var inError = false for line <- errorsText.linesIterator do diff --git a/sbt-bridge/src/dotty/tools/xsbt/DelegatingReporter.java b/sbt-bridge/src/dotty/tools/xsbt/DelegatingReporter.java index 20cdfb720538..3e1e291ab7d1 100644 --- a/sbt-bridge/src/dotty/tools/xsbt/DelegatingReporter.java +++ b/sbt-bridge/src/dotty/tools/xsbt/DelegatingReporter.java @@ -45,7 +45,7 @@ public void doReport(Diagnostic dia, Context ctx) { rendered.append(explanation(message, ctx)); } - delegate.log(new Problem(position, message.msg(), severity, rendered.toString(), diagnosticCode)); + delegate.log(new Problem(position, message.message(), severity, rendered.toString(), diagnosticCode)); } private static Severity severityOf(int level) { diff --git a/scaladoc/test/dotty/tools/scaladoc/testUtils.scala b/scaladoc/test/dotty/tools/scaladoc/testUtils.scala index 21ed7398f74e..2ba78c321eab 100644 --- a/scaladoc/test/dotty/tools/scaladoc/testUtils.scala +++ b/scaladoc/test/dotty/tools/scaladoc/testUtils.scala @@ -11,9 +11,9 @@ import java.nio.file.Paths case class ReportedDiagnostics(errors: List[Diagnostic], warnings: List[Diagnostic], infos: List[Diagnostic]): - def errorMsgs = errors.map(_.msg.rawMessage) - def warningMsgs = warnings.map(_.msg.rawMessage) - def infoMsgs = infos.map(_.msg.rawMessage) + def errorMsgs = errors.map(_.msg.message) + def warningMsgs = warnings.map(_.msg.message) + def infoMsgs = infos.map(_.msg.message) extension (c: CompilerContext) def reportedDiagnostics: ReportedDiagnostics = diff --git a/tests/neg-custom-args/i13838.check b/tests/neg-custom-args/i13838.check index db4be5f021af..5e62779f3238 100644 --- a/tests/neg-custom-args/i13838.check +++ b/tests/neg-custom-args/i13838.check @@ -1,15 +1,15 @@ -- [E172] Type Error: tests/neg-custom-args/i13838.scala:10:5 ---------------------------------------------------------- 10 | foo // error | ^ - |No given instance of type Order[X] was found for parameter x$1 of method foo in object FooT - | - |where: X is a type variable - |. + |No given instance of type Order[X] was found for parameter x$1 of method foo in object FooT. |I found: | | FooT.OrderFFooA[F, A](FooT.OrderFFooA[F, A](/* missing */summon[Order[F[Foo[A]]]])) | - |But given instance OrderFFooA in object FooT produces a diverging implicit search when trying to match type Order[F[Foo[A]]]. + |But given instance OrderFFooA in object FooT produces a diverging implicit search when trying to match type Order[F[Foo[A]]] + | + |where: X is a type variable + |. -- [E168] Type Warning: tests/neg-custom-args/i13838.scala:10:5 -------------------------------------------------------- 10 | foo // error | ^ diff --git a/tests/neg/exports.check b/tests/neg/exports.check index 49d8cdf0654b..79951cebfc39 100644 --- a/tests/neg/exports.check +++ b/tests/neg/exports.check @@ -11,7 +11,7 @@ 25 | export printUnit.bitmap // error: no eligible member | ^ | non-private given instance bitmap in class Copier refers to private value printUnit - | in its type signature => Copier.this.printUnit.bitmap + | in its type signature => object Copier.this.printUnit.bitmap -- [E120] Naming Error: tests/neg/exports.scala:23:33 ------------------------------------------------------------------ 23 | export printUnit.{stat => _, _} // error: double definition | ^ diff --git a/tests/neg/i15507.check b/tests/neg/i15507.check new file mode 100644 index 000000000000..3786d559c306 --- /dev/null +++ b/tests/neg/i15507.check @@ -0,0 +1,40 @@ +-- Error: tests/neg/i15507.scala:2:40 ---------------------------------------------------------------------------------- +2 | type _NestedSet1[X] = Set[_NestedSet1[?]] // error + | ^ + | no wildcard type allowed here +-- Error: tests/neg/i15507.scala:3:41 ---------------------------------------------------------------------------------- +3 | type _NestedSet2[X] <: Set[_NestedSet2[?]] // error + | ^ + | no wildcard type allowed here +-- [E140] Cyclic Error: tests/neg/i15507.scala:5:7 --------------------------------------------------------------------- +5 | type _NestedSet4[X] >: Set[_NestedSet4[X]] // error + | ^ + | illegal cyclic type reference: lower bound ... of type _NestedSet4 refers back to the type itself +-- [E140] Cyclic Error: tests/neg/i15507.scala:6:7 --------------------------------------------------------------------- +6 | type _NestedSet5[X] = Set[_NestedSet5[X]] // error + | ^ + | illegal cyclic type reference: alias ... of type _NestedSet5 refers back to the type itself +-- [E140] Cyclic Error: tests/neg/i15507.scala:7:7 --------------------------------------------------------------------- +7 | type _NestedSet6[X] = Set[_NestedSet6[Int]] // error + | ^ + | illegal cyclic type reference: alias ... of type _NestedSet6 refers back to the type itself +-- Error: tests/neg/i15507.scala:9:43 ---------------------------------------------------------------------------------- +9 | type _NestedList1[X] = List[_NestedList1[?]] // error + | ^ + | no wildcard type allowed here +-- Error: tests/neg/i15507.scala:10:44 --------------------------------------------------------------------------------- +10 | type _NestedList2[X] <: List[_NestedList2[?]] // error + | ^ + | no wildcard type allowed here +-- [E140] Cyclic Error: tests/neg/i15507.scala:12:7 -------------------------------------------------------------------- +12 | type _NestedList4[X] >: List[_NestedList4[X]] // error + | ^ + | illegal cyclic type reference: lower bound ... of type _NestedList4 refers back to the type itself +-- [E140] Cyclic Error: tests/neg/i15507.scala:13:7 -------------------------------------------------------------------- +13 | type _NestedList5[X] = List[_NestedList5[X]] // error + | ^ + | illegal cyclic type reference: alias ... of type _NestedList5 refers back to the type itself +-- [E140] Cyclic Error: tests/neg/i15507.scala:14:7 -------------------------------------------------------------------- +14 | type _NestedList6[X] = List[_NestedList6[Int]] // error + | ^ + | illegal cyclic type reference: alias ... of type _NestedList6 refers back to the type itself diff --git a/tests/neg/i15507.scala b/tests/neg/i15507.scala index 3c45f2a8d9f6..f65d216ba6a2 100644 --- a/tests/neg/i15507.scala +++ b/tests/neg/i15507.scala @@ -1,12 +1,12 @@ object TestNested: - type _NestedSet1[X] = Set[_NestedSet1[?]] // error // error + type _NestedSet1[X] = Set[_NestedSet1[?]] // error type _NestedSet2[X] <: Set[_NestedSet2[?]] // error type _NestedSet3[X] <: Set[_NestedSet3[X]] // ok type _NestedSet4[X] >: Set[_NestedSet4[X]] // error type _NestedSet5[X] = Set[_NestedSet5[X]] // error type _NestedSet6[X] = Set[_NestedSet6[Int]] // error - type _NestedList1[X] = List[_NestedList1[?]] // error // error + type _NestedList1[X] = List[_NestedList1[?]] // error type _NestedList2[X] <: List[_NestedList2[?]] // error type _NestedList3[X] <: List[_NestedList3[X]] // ok type _NestedList4[X] >: List[_NestedList4[X]] // error diff --git a/tests/neg/i827.check b/tests/neg/i827.check new file mode 100644 index 000000000000..825aefbb480b --- /dev/null +++ b/tests/neg/i827.check @@ -0,0 +1,11 @@ +-- [E069] Naming Error: tests/neg/i827.scala:3:8 ----------------------------------------------------------------------- +3 | trait Inner extends self.Inner // error: cannot merge trait Inner in trait A with trait Inner in trait B as members of type (A & B)(B.this) + | ^ + |trait Inner cannot have the same name as trait Inner in trait A -- cannot define trait member with the same name as a trait member in self reference self. + |(Note: this can be resolved by using another name) +-- [E110] Syntax Error: tests/neg/i827.scala:7:16 ---------------------------------------------------------------------- +7 |class C extends C // error: cyclic inheritance: class C extends itself + | ^ + | Cyclic inheritance: class C extends itself + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i8623.check b/tests/neg/i8623.check index b9d6e244e70e..39337a7839d8 100644 --- a/tests/neg/i8623.check +++ b/tests/neg/i8623.check @@ -8,4 +8,6 @@ | This might be because resolution yielded as given instance a function that is not | known to be total and side-effect free. | + | where: ?1 is an unknown value of type QC + | | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i9568.check b/tests/neg/i9568.check index 3d03a6f1db63..3f318d0b0111 100644 --- a/tests/neg/i9568.check +++ b/tests/neg/i9568.check @@ -1,12 +1,16 @@ -- [E172] Type Error: tests/neg/i9568.scala:13:10 ---------------------------------------------------------------------- 13 | blaMonad.foo(bla) // error: diverges | ^ - | No given instance of type => Monad[F] was found for parameter ev of method blaMonad in object Test - | - | where: F is a type variable with constraint <: [_] =>> Any - | . + | No given instance of type => Monad[F] was found for parameter ev of method blaMonad in object Test. | I found: | - | Test.blaMonad[F, S](Test.blaMonad[F, S]) + | Test.blaMonad[F², S](Test.blaMonad[F³, S²]) | - | But method blaMonad in object Test does not match type => Monad[F]. + | But method blaMonad in object Test does not match type => Monad[F²] + | + | where: F is a type variable with constraint <: [_] =>> Any + | F² is a type variable with constraint <: [_] =>> Any + | F³ is a type variable with constraint <: [_] =>> Any + | S is a type variable + | S² is a type variable + | . From 3ace8878eeb13d3024de59fe57691cb8186775da Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 23 Nov 2022 21:32:16 +0100 Subject: [PATCH 07/14] Cleanups of Message --- .../dotty/tools/dotc/reporting/Message.scala | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index e5a654027daf..dc042ce20544 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -34,7 +34,9 @@ object Message: var nonSensical = false private var recordOK = disambiguate - def stopRecording() = + /** Clear all entries and stop further entries to be added */ + def disable() = + clear() recordOK = false def record(str: String, isType: Boolean, entry: Recorded)(using Context): String = { @@ -240,10 +242,10 @@ abstract class Message(val errorId: ErrorMessageID)(using Context) { self => case msgPrinter: Message.Printer => myIsNonSensical = msgPrinter.seen.nonSensical val addendum = explanations(msgPrinter.seen) - msgPrinter.seen.clear() - msgPrinter.seen.stopRecording() - if addendum.isEmpty || !disambiguate then "" - else "\n\n" ++ addendum + msgPrinter.seen.disable() + // Clear entries and stop futher recording so that messages containing the current + // one don't repeat the explanations or use explanations from the msgPostscript. + if addendum.isEmpty then "" else "\n\n" ++ addendum case _ => "" @@ -254,7 +256,7 @@ abstract class Message(val errorId: ErrorMessageID)(using Context) { self => */ def canExplain: Boolean = explain.nonEmpty - var myIsNonSensical: Boolean = false + private var myIsNonSensical: Boolean = false /** A message is non-sensical if it contains references to internally * generated error types. Normally we want to suppress error messages @@ -272,14 +274,14 @@ abstract class Message(val errorId: ErrorMessageID)(using Context) { self => private def inMessageContext(disambiguate: Boolean)(op: Context ?=> String): String = if ctx eq NoContext then op else - val (msgContext, msgPrinter: Message.Printer) = ctx.printer match - case printer: Message.Printer => (ctx, printer) + val msgContext = ctx.printer match + case _: Message.Printer => ctx case _ => val seen = Seen(disambiguate) val ctx1 = ctx.fresh.setPrinterFn(Message.Printer(seen, _)) if !ctx1.property(MessageLimiter).isDefined then ctx1.setProperty(MessageLimiter, ErrorMessageLimiter()) - (ctx1, ctx1.printer.asInstanceOf[Message.Printer]) + ctx1 op(using msgContext) /** The message to report. tags are filtered out */ From f169753e79f286d35fb49dc79e43fc767c30aaf7 Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 23 Nov 2022 21:52:09 +0100 Subject: [PATCH 08/14] Add doc comments --- .../dotty/tools/dotc/core/Decorators.scala | 3 +++ .../dotty/tools/dotc/reporting/Message.scala | 25 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/core/Decorators.scala b/compiler/src/dotty/tools/dotc/core/Decorators.scala index 051b3295c3f2..3c9399487a71 100644 --- a/compiler/src/dotty/tools/dotc/core/Decorators.scala +++ b/compiler/src/dotty/tools/dotc/core/Decorators.scala @@ -58,6 +58,9 @@ object Decorators { padding + s.replace("\n", "\n" + padding) end extension + /** Convert lazy string to message. To be with caution, since no message-defined + * formatting will be done on the string. + */ extension (str: => String) def toMessage: Message = NoExplanation(str)(using NoContext) diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index dc042ce20544..d360db68a8c0 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -208,6 +208,31 @@ end Message * * @param errorId a unique id identifying the message, this will be * used to reference documentation online + * + * Messages modify the rendendering of interpolated strings in several ways: + * + * 1. The size of the printed code is limited with a MessafeLimiter. If the message + * would get too large or too deeply nested, a `...` is printed instead. + * 2. References to module classes are prefixed with `object ` for better recogniability. + * 3. A where clause is sometimes added which contains the following additional explanations: + * - Rerences are disambiguated: If a message contains occurrences of the same identifier + * representing different symbols, the duplicates are printed with superscripts + * and the where-clause explains where each symbol is located. + * - Uninstantiated variables are explained in the where-clause with additional + * info about their bounds. + * - Skolems are explained with additional info about their underlying type. + * + * Messages inheriting from the NoDisambiguation trait or returned from the + * `noDisambiguation()` method skip point (3) above. This makes sense if the + * message already exolains where different occurrences of the same identifier + * are located. Examples are NamingMsgs such as double definition errors, + * overriding errors, and ambiguous implicit errors. + * + * We consciously made the design decision to disambiguate by default and disable + * disambiguation as an opt-in. The reason is that one usually does not consider all + * fine-grained details when writing an error message. If disambiguation is the default, + * some tests will show where clauses that look too noisy and that then can be disabled + * when needed. But */ abstract class Message(val errorId: ErrorMessageID)(using Context) { self => import Message.* From fa3269c38377a5b5a93810b257e410559a5de551 Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 23 Nov 2022 22:14:27 +0100 Subject: [PATCH 09/14] Drop e, ex and exm interpolators That leaves just two interpolators: - i for strings - em for error messages (and other diagnostics) --- .../tools/backend/jvm/BCodeHelpers.scala | 2 +- .../dotty/tools/backend/sjs/JSCodeGen.scala | 2 +- .../dotty/tools/dotc/core/Decorators.scala | 37 +- .../dotc/inlines/PrepareInlineable.scala | 2 +- .../dotty/tools/dotc/reporting/messages.scala | 547 +++++++++--------- .../dotty/tools/dotc/transform/Erasure.scala | 2 +- .../tools/dotc/transform/TreeChecker.scala | 14 +- .../src/dotty/tools/dotc/typer/Checking.scala | 6 +- .../dotty/tools/dotc/typer/TypeAssigner.scala | 2 +- .../src/dotty/tools/dotc/typer/Typer.scala | 8 +- 10 files changed, 296 insertions(+), 326 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala b/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala index c49b7d9556c9..33da88170ee1 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala @@ -422,7 +422,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { emitAssocs(nestedVisitor, assocs, bcodeStore)(innerClasesStore) case t => - report.error(ex"Annotation argument is not a constant", t.sourcePos) + report.error(em"Annotation argument is not a constant", t.sourcePos) } } diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index 6714f664620b..45a1cf8d3758 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -2926,7 +2926,7 @@ class JSCodeGen()(using genCtx: Context) { case defn.ArrayOf(el) => el case JavaArrayType(el) => el case tpe => - val msg = ex"expected Array $tpe" + val msg = em"expected Array $tpe" report.error(msg) ErrorType(msg) } diff --git a/compiler/src/dotty/tools/dotc/core/Decorators.scala b/compiler/src/dotty/tools/dotc/core/Decorators.scala index 3c9399487a71..0a48ad7a899c 100644 --- a/compiler/src/dotty/tools/dotc/core/Decorators.scala +++ b/compiler/src/dotty/tools/dotc/core/Decorators.scala @@ -296,46 +296,17 @@ object Decorators { def derivedCons(x1: T, xs1: List[T]) = if (xs.head eq x1) && (xs.tail eq xs1) then xs else x1 :: xs1 - /* This extension defines the following string formatters: - * i, e, ex, em, exm - * String formatters ending in `m` produce a message, the others produce a string. - * Details are as follows: - * - * i : Gneral purpose string interpolator that uses Formtting.show to print compiler data - * e : Like `i`, but with logic that limits the size of messages and that - * filters out nonsensical messages after the first error is reported. - * `e` or its variants should be used for error messages and warnings instead of `i`. - * ex : Like `e`, but it assembles additional info on type variables and for - * disambiguating symbols. - * em : Like `e`, but producing a message - * exm: Like `ex`, but producing a message - */ extension (sc: StringContext) + /** General purpose string formatting */ def i(args: Shown*)(using Context): String = new StringFormatter(sc).assemble(args) - /** Formatting for error messages: Like `i`, with two modifications - * - limit size of messages - * - mark some parts of messages with tags, so that - * error messages after the first one are filtered out if some of - * their arguments are "non-sensical". + /** Interpolator yielding an error message, which undergoes + * the formatting defined in Message. */ - def e(args: Shown*)(using Context): String = - new StringFormatter(sc).assemble(args) - - /** A NoExplanation message formatted with `e` */ def em(args: Shown*)(using Context): NoExplanation = - NoExplanation(e(args*)) - - def exm(args: Shown*)(using Context): NoExplanation = - NoExplanation(ex(args*)) - - /** Formatting with added explanations: Like `em`, but add explanations to - * give more info about type variables and to disambiguate where needed. - */ - def ex(args: Shown*)(using Context): String = - new StringFormatter(sc).assemble(args) + NoExplanation(i(args*)) extension [T <: AnyRef](arr: Array[T]) def binarySearch(x: T | Null): Int = java.util.Arrays.binarySearch(arr.asInstanceOf[Array[Object | Null]], x) diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index 7e47bbfdfa8a..85293d4a82d7 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -284,7 +284,7 @@ object PrepareInlineable { private def checkInlineMethod(inlined: Symbol, body: Tree)(using Context): body.type = { if Inlines.inInlineMethod(using ctx.outer) then - report.error(ex"Implementation restriction: nested inline methods are not supported", inlined.srcPos) + report.error(em"Implementation restriction: nested inline methods are not supported", inlined.srcPos) if (inlined.is(Macro) && !ctx.isAfterTyper) { diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 477192c2402e..a3356c1f7021 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -102,7 +102,7 @@ extends SyntaxMsg(errNo) { | // perform your cleanup here! |}""".stripMargin - e"""|A ${hl("try")} expression should be followed by some mechanism to handle any exceptions + i"""|A ${hl("try")} expression should be followed by some mechanism to handle any exceptions |thrown. Typically a ${hl("catch")} expression follows the ${hl("try")} and pattern matches |on any expected exceptions. For example: | @@ -121,23 +121,23 @@ extends SyntaxMsg(errNo) { class EmptyCatchBlock(tryBody: untpd.Tree)(using Context) extends EmptyCatchOrFinallyBlock(tryBody, EmptyCatchBlockID) { def msg(using Context) = - e"""|The ${hl("catch")} block does not contain a valid expression, try + i"""|The ${hl("catch")} block does not contain a valid expression, try |adding a case like - ${hl("case e: Exception =>")} to the block""" } class EmptyCatchAndFinallyBlock(tryBody: untpd.Tree)(using Context) extends EmptyCatchOrFinallyBlock(tryBody, EmptyCatchAndFinallyBlockID) { def msg(using Context) = - e"""|A ${hl("try")} without ${hl("catch")} or ${hl("finally")} is equivalent to putting + i"""|A ${hl("try")} without ${hl("catch")} or ${hl("finally")} is equivalent to putting |its body in a block; no exceptions are handled.""" } class DeprecatedWithOperator()(using Context) extends SyntaxMsg(DeprecatedWithOperatorID) { def msg(using Context) = - e"""${hl("with")} as a type operator has been deprecated; use ${hl("&")} instead""" + i"""${hl("with")} as a type operator has been deprecated; use ${hl("&")} instead""" def explain(using Context) = - e"""|Dotty introduces intersection types - ${hl("&")} types. These replace the + i"""|Dotty introduces intersection types - ${hl("&")} types. These replace the |use of the ${hl("with")} keyword. There are a few differences in |semantics between intersection types and using ${hl("with")}.""" } @@ -145,10 +145,10 @@ extends SyntaxMsg(DeprecatedWithOperatorID) { class CaseClassMissingParamList(cdef: untpd.TypeDef)(using Context) extends SyntaxMsg(CaseClassMissingParamListID) { def msg(using Context) = - e"""|A ${hl("case class")} must have at least one parameter list""" + i"""|A ${hl("case class")} must have at least one parameter list""" def explain(using Context) = - e"""|${cdef.name} must have at least one parameter list, if you would rather + i"""|${cdef.name} must have at least one parameter list, if you would rather |have a singleton representation of ${cdef.name}, use a "${hl("case object")}". |Or, add an explicit ${hl("()")} as a parameter list to ${cdef.name}.""" } @@ -196,7 +196,7 @@ extends SyntaxMsg(WildcardOnTypeArgumentNotAllowedOnNewID) { | val team = new Team[Int] |} """.stripMargin - e"""|Wildcard on arguments is not allowed when declaring a new type. + i"""|Wildcard on arguments is not allowed when declaring a new type. | |Given the following example: | @@ -211,7 +211,7 @@ extends SyntaxMsg(WildcardOnTypeArgumentNotAllowedOnNewID) { // Type Errors ------------------------------------------------------------ // class DuplicateBind(bind: untpd.Bind, tree: untpd.CaseDef)(using Context) extends NamingMsg(DuplicateBindID) { - def msg(using Context) = e"duplicate pattern variable: ${bind.name}" + def msg(using Context) = i"duplicate pattern variable: ${bind.name}" def explain(using Context) = { val pat = tree.pat.show @@ -226,7 +226,7 @@ extends NamingMsg(DuplicateBindID) { val caseDef = s"case $pat$guard => $body" - e"""|For each ${hl("case")} bound variable names have to be unique. In: + i"""|For each ${hl("case")} bound variable names have to be unique. In: | |$caseDef | @@ -236,9 +236,9 @@ extends NamingMsg(DuplicateBindID) { class MissingIdent(tree: untpd.Ident, treeKind: String, val name: Name)(using Context) extends NotFoundMsg(MissingIdentID) { - def msg(using Context) = e"Not found: $treeKind$name" + def msg(using Context) = i"Not found: $treeKind$name" def explain(using Context) = { - e"""|The identifier for `$treeKind$name` is not bound, that is, + i"""|The identifier for `$treeKind$name` is not bound, that is, |no declaration for this identifier can be found. |That can happen, for example, if `$name` or its declaration has either been |misspelt or if an import is missing.""" @@ -370,7 +370,7 @@ extends NotFoundMsg(NotAMemberID), ShowMatchTrace(site) { s" - did you mean $siteName.$showName?$enumClause" case Nil => prefixEnumClause("") - ex"$selected $name is not a member of ${site.widen}$finalAddendum" + i"$selected $name is not a member of ${site.widen}$finalAddendum" } def explain(using Context) = "" @@ -407,7 +407,7 @@ extends SyntaxMsg(EarlyDefinitionsNotSupportedID) { | |class C extends Logging(new File("log.data"))""".stripMargin - e"""|Earlier versions of Scala did not support trait parameters and "early + i"""|Earlier versions of Scala did not support trait parameters and "early |definitions" (also known as "early initializers") were used as an alternative. | |Example of old syntax: @@ -423,7 +423,7 @@ extends SyntaxMsg(EarlyDefinitionsNotSupportedID) { class TopLevelImplicitClass(cdef: untpd.TypeDef)(using Context) extends SyntaxMsg(TopLevelImplicitClassID) { - def msg(using Context) = e"""An ${hl("implicit class")} may not be top-level""" + def msg(using Context) = i"""An ${hl("implicit class")} may not be top-level""" def explain(using Context) = { val TypeDef(name, impl @ Template(constr0, parents, self, _)) = cdef: @unchecked @@ -432,7 +432,7 @@ extends SyntaxMsg(TopLevelImplicitClassID) { else constr0.termParamss(0).map(_.withMods(untpd.Modifiers()).show).mkString(", ") def defHasBody[T] = impl.body.exists(!_.isEmpty) val exampleBody = if (defHasBody) "{\n ...\n }" else "" - e"""|There may not be any method, member or object in scope with the same name as + i"""|There may not be any method, member or object in scope with the same name as |the implicit class and a case class automatically gets a companion object with |the same name created by the compiler which would cause a naming conflict if it |were allowed. @@ -451,10 +451,10 @@ extends SyntaxMsg(TopLevelImplicitClassID) { class ImplicitCaseClass(cdef: untpd.TypeDef)(using Context) extends SyntaxMsg(ImplicitCaseClassID) { - def msg(using Context) = e"""A ${hl("case class")} may not be defined as ${hl("implicit")}""" + def msg(using Context) = i"""A ${hl("case class")} may not be defined as ${hl("implicit")}""" def explain(using Context) = - e"""|Implicit classes may not be case classes. Instead use a plain class: + i"""|Implicit classes may not be case classes. Instead use a plain class: | |implicit class ${cdef.name}... | @@ -466,7 +466,7 @@ extends SyntaxMsg(ImplicitClassPrimaryConstructorArityID){ def msg(using Context) = "Implicit classes must accept exactly one primary constructor parameter" def explain(using Context) = { val example = "implicit class RichDate(date: java.util.Date)" - e"""Implicit classes may only take one non-implicit argument in their constructor. For example: + i"""Implicit classes may only take one non-implicit argument in their constructor. For example: | | $example | @@ -478,12 +478,12 @@ extends SyntaxMsg(ImplicitClassPrimaryConstructorArityID){ class ObjectMayNotHaveSelfType(mdef: untpd.ModuleDef)(using Context) extends SyntaxMsg(ObjectMayNotHaveSelfTypeID) { - def msg(using Context) = e"""${hl("object")}s must not have a self ${hl("type")}""" + def msg(using Context) = i"""${hl("object")}s must not have a self ${hl("type")}""" def explain(using Context) = { val untpd.ModuleDef(name, tmpl) = mdef val ValDef(_, selfTpt, _) = tmpl.self - e"""|${hl("object")}s must not have a self ${hl("type")}: + i"""|${hl("object")}s must not have a self ${hl("type")}: | |Consider these alternative solutions: | - Create a trait or a class instead of an object @@ -495,12 +495,12 @@ extends SyntaxMsg(ObjectMayNotHaveSelfTypeID) { class RepeatedModifier(modifier: String)(implicit ctx:Context) extends SyntaxMsg(RepeatedModifierID) { - def msg(using Context) = e"""Repeated modifier $modifier""" + def msg(using Context) = i"""Repeated modifier $modifier""" def explain(using Context) = { val code1 = "private private val Origin = Point(0, 0)" val code2 = "private final val Origin = Point(0, 0)" - e"""This happens when you accidentally specify the same modifier twice. + i"""This happens when you accidentally specify the same modifier twice. | |Example: | @@ -520,7 +520,7 @@ extends SyntaxMsg(InterpolatedStringErrorID) { def explain(using Context) = { val code1 = "s\"$new Point(0, 0)\"" val code2 = "s\"${new Point(0, 0)}\"" - e"""|This usually happens when you forget to place your expressions inside curly braces. + i"""|This usually happens when you forget to place your expressions inside curly braces. | |$code1 | @@ -533,9 +533,9 @@ extends SyntaxMsg(InterpolatedStringErrorID) { class UnboundPlaceholderParameter()(implicit ctx:Context) extends SyntaxMsg(UnboundPlaceholderParameterID) { - def msg(using Context) = e"""Unbound placeholder parameter; incorrect use of ${hl("_")}""" + def msg(using Context) = i"""Unbound placeholder parameter; incorrect use of ${hl("_")}""" def explain(using Context) = - e"""|The ${hl("_")} placeholder syntax was used where it could not be bound. + i"""|The ${hl("_")} placeholder syntax was used where it could not be bound. |Consider explicitly writing the variable binding. | |This can be done by replacing ${hl("_")} with a variable (eg. ${hl("x")}) @@ -579,9 +579,9 @@ extends SyntaxMsg(UnboundPlaceholderParameterID) { class IllegalStartSimpleExpr(illegalToken: String)(using Context) extends SyntaxMsg(IllegalStartSimpleExprID) { - def msg(using Context) = e"expression expected but ${Red(illegalToken)} found" + def msg(using Context) = i"expression expected but ${Red(illegalToken)} found" def explain(using Context) = { - e"""|An expression cannot start with ${Red(illegalToken)}.""" + i"""|An expression cannot start with ${Red(illegalToken)}.""" } } @@ -589,7 +589,7 @@ class MissingReturnType()(implicit ctx:Context) extends SyntaxMsg(MissingReturnTypeID) { def msg(using Context) = "Missing return type" def explain(using Context) = - e"""|An abstract declaration must have a return type. For example: + i"""|An abstract declaration must have a return type. For example: | |trait Shape: | ${hl("def area: Double")} // abstract declaration returning a Double""" @@ -597,9 +597,9 @@ extends SyntaxMsg(MissingReturnTypeID) { class MissingReturnTypeWithReturnStatement(method: Symbol)(using Context) extends SyntaxMsg(MissingReturnTypeWithReturnStatementID) { - def msg(using Context) = e"$method has a return statement; it needs a result type" + def msg(using Context) = i"$method has a return statement; it needs a result type" def explain(using Context) = - e"""|If a method contains a ${hl("return")} statement, it must have an + i"""|If a method contains a ${hl("return")} statement, it must have an |explicit return type. For example: | |${hl("def good: Int /* explicit return type */ = return 1")}""" @@ -607,10 +607,10 @@ extends SyntaxMsg(MissingReturnTypeWithReturnStatementID) { class YieldOrDoExpectedInForComprehension()(using Context) extends SyntaxMsg(YieldOrDoExpectedInForComprehensionID) { - def msg(using Context) = e"${hl("yield")} or ${hl("do")} expected" + def msg(using Context) = i"${hl("yield")} or ${hl("do")} expected" def explain(using Context) = - e"""|When the enumerators in a for comprehension are not placed in parentheses or + i"""|When the enumerators in a for comprehension are not placed in parentheses or |braces, a ${hl("do")} or ${hl("yield")} statement is required after the enumerators |section of the comprehension. | @@ -640,7 +640,7 @@ extends SyntaxMsg(YieldOrDoExpectedInForComprehensionID) { class ProperDefinitionNotFound()(using Context) extends Message(ProperDefinitionNotFoundID) { def kind = MessageKind.DocComment - def msg(using Context) = e"""Proper definition was not found in ${hl("@usecase")}""" + def msg(using Context) = i"""Proper definition was not found in ${hl("@usecase")}""" def explain(using Context) = { val noUsecase = @@ -654,7 +654,7 @@ extends Message(ProperDefinitionNotFoundID) { |def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That |""".stripMargin - e"""|Usecases are only supported for ${hl("def")}s. They exist because with Scala's + i"""|Usecases are only supported for ${hl("def")}s. They exist because with Scala's |advanced type-system, we sometimes end up with seemingly scary signatures. |The usage of these methods, however, needs not be - for instance the ${hl("map")} |function @@ -678,10 +678,10 @@ extends Message(ProperDefinitionNotFoundID) { class ByNameParameterNotSupported(tpe: untpd.Tree)(using Context) extends SyntaxMsg(ByNameParameterNotSupportedID) { - def msg(using Context) = e"By-name parameter type ${tpe} not allowed here." + def msg(using Context) = i"By-name parameter type ${tpe} not allowed here." def explain(using Context) = - e"""|By-name parameters act like functions that are only evaluated when referenced, + i"""|By-name parameters act like functions that are only evaluated when referenced, |allowing for lazy evaluation of a parameter. | |An example of using a by-name parameter would look like: @@ -716,7 +716,7 @@ extends SyntaxMsg(WrongNumberOfTypeArgsID) { case NoSymbol => fntpe.show case symbol => symbol.showFullName catch case NonFatal(ex) => fntpe.show - e"""|$msgPrefix type arguments for $prettyName$expectedArgString + i"""|$msgPrefix type arguments for $prettyName$expectedArgString |expected: $expectedArgString |actual: $actualArgString""" @@ -726,7 +726,7 @@ extends SyntaxMsg(WrongNumberOfTypeArgsID) { |val list: List[(Int, String)] = List(tuple2)""".stripMargin if (actualCount > expectedCount) - e"""|You have supplied too many type parameters + i"""|You have supplied too many type parameters | |For example List takes a single type parameter (List[A]) |If you need to hold more types in a list then you need to combine them @@ -735,14 +735,14 @@ extends SyntaxMsg(WrongNumberOfTypeArgsID) { | |${tooManyTypeParams}""" else - e"""|You have not supplied enough type parameters + i"""|You have not supplied enough type parameters |If you specify one type parameter then you need to specify every type parameter.""" } } class IllegalVariableInPatternAlternative(name: Name)(using Context) extends SyntaxMsg(IllegalVariableInPatternAlternativeID) { - def msg(using Context) = e"Illegal variable $name in pattern alternative" + def msg(using Context) = i"Illegal variable $name in pattern alternative" def explain(using Context) = { val varInAlternative = """|def g(pair: (Int,Int)): Int = pair match { @@ -757,7 +757,7 @@ extends SyntaxMsg(IllegalVariableInPatternAlternativeID) { | case _ => 0 |}""".stripMargin - e"""|Variables are not allowed within alternate pattern matches. You can workaround + i"""|Variables are not allowed within alternate pattern matches. You can workaround |this issue by adding additional cases for each alternative. For example, the |illegal function: | @@ -772,9 +772,9 @@ class IdentifierExpected(identifier: String)(using Context) extends SyntaxMsg(IdentifierExpectedID) { def msg(using Context) = "identifier expected" def explain(using Context) = { - val wrongIdentifier = e"def foo: $identifier = {...}" - val validIdentifier = e"def foo = {...}" - e"""|An identifier expected, but $identifier found. This could be because + val wrongIdentifier = i"def foo: $identifier = {...}" + val validIdentifier = i"def foo = {...}" + i"""|An identifier expected, but $identifier found. This could be because |$identifier is not a valid identifier. As a workaround, the compiler could |infer the type for you. For example, instead of: | @@ -792,7 +792,7 @@ class AuxConstructorNeedsNonImplicitParameter()(implicit ctx:Context) extends SyntaxMsg(AuxConstructorNeedsNonImplicitParameterID) { def msg(using Context) = "Auxiliary constructor needs non-implicit parameter list" def explain(using Context) = - e"""|Only the primary constructor is allowed an ${hl("implicit")} parameter list; + i"""|Only the primary constructor is allowed an ${hl("implicit")} parameter list; |auxiliary constructors need non-implicit parameter lists. When a primary |constructor has an implicit argslist, auxiliary constructors that call the |primary constructor must specify the implicit value. @@ -807,7 +807,7 @@ class IllegalLiteral()(using Context) extends SyntaxMsg(IllegalLiteralID) { def msg(using Context) = "Illegal literal" def explain(using Context) = - e"""|Available literals can be divided into several groups: + i"""|Available literals can be divided into several groups: | - Integer literals: 0, 21, 0xFFFFFFFF, -42L | - Floating Point Literals: 0.0, 1e30f, 3.14159f, 1.0e-100, .1 | - Boolean Literals: true, false @@ -820,7 +820,7 @@ extends SyntaxMsg(IllegalLiteralID) { class LossyWideningConstantConversion(sourceType: Type, targetType: Type)(using Context) extends Message(LossyWideningConstantConversionID): def kind = MessageKind.LossyConversion - def msg(using Context) = e"""|Widening conversion from $sourceType to $targetType loses precision. + def msg(using Context) = i"""|Widening conversion from $sourceType to $targetType loses precision. |Write `.to$targetType` instead.""" def explain(using Context) = "" @@ -830,14 +830,14 @@ extends Message(PatternMatchExhaustivityID) { lazy val uncovered = uncoveredFn def msg(using Context) = val addendum = if hasMore then "(More unmatched cases are elided)" else "" - e"""|${hl("match")} may not be exhaustive. + i"""|${hl("match")} may not be exhaustive. | |It would fail on pattern case: $uncovered |$addendum""" def explain(using Context) = - e"""|There are several ways to make the match exhaustive: + i"""|There are several ways to make the match exhaustive: | - Add missing cases as shown in the warning | - If an extractor always return ${hl("Some(...)")}, write ${hl("Some[X]")} for its return type | - Add a ${hl("case _ => ...")} at the end to match all remaining cases @@ -848,7 +848,7 @@ class UncheckedTypePattern(msgFn: => String)(using Context) extends PatternMatchMsg(UncheckedTypePatternID) { def msg(using Context) = msgFn def explain(using Context) = - e"""|Type arguments and type refinements are erased during compile time, thus it's + i"""|Type arguments and type refinements are erased during compile time, thus it's |impossible to check them at run-time. | |You can either replace the type arguments by ${hl("_")} or use `@unchecked`. @@ -864,7 +864,7 @@ extends Message(MatchCaseUnreachableID) { class MatchCaseOnlyNullWarning()(using Context) extends PatternMatchMsg(MatchCaseOnlyNullWarningID) { - def msg(using Context) = e"""Unreachable case except for ${hl("null")} (if this is intentional, consider writing ${hl("case null =>")} instead).""" + def msg(using Context) = i"""Unreachable case except for ${hl("null")} (if this is intentional, consider writing ${hl("case null =>")} instead).""" def explain(using Context) = "" } @@ -872,12 +872,12 @@ class MatchableWarning(tp: Type, pattern: Boolean)(using Context) extends TypeMsg(MatchableWarningID) { def msg(using Context) = val kind = if pattern then "pattern selector" else "value" - e"""${kind} should be an instance of Matchable,, + i"""${kind} should be an instance of Matchable,, |but it has unmatchable type $tp instead""" def explain(using Context) = if pattern then - e"""A value of type $tp cannot be the selector of a match expression + i"""A value of type $tp cannot be the selector of a match expression |since it is not constrained to be `Matchable`. Matching on unconstrained |values is disallowed since it can uncover implementation details that |were intended to be hidden and thereby can violate paramtetricity laws @@ -890,20 +890,20 @@ extends TypeMsg(MatchableWarningID) { | import compiletime.asMatchable | def f[X](x: X) = x.asMatchable match { ... }""" else - e"""The value can be converted to a `Matchable` by appending `.asMatchable`. + i"""The value can be converted to a `Matchable` by appending `.asMatchable`. |`asMatchable` needs to be imported from scala.compiletime.""" } class SeqWildcardPatternPos()(using Context) extends SyntaxMsg(SeqWildcardPatternPosID) { - def msg(using Context) = e"""${hl("*")} can be used only for last argument""" + def msg(using Context) = i"""${hl("*")} can be used only for last argument""" def explain(using Context) = { val code = """def sumOfTheFirstTwo(list: List[Int]): Int = list match { | case List(first, second, x*) => first + second | case _ => 0 |}""" - e"""|Sequence wildcard pattern is expected at the end of an argument list. + i"""|Sequence wildcard pattern is expected at the end of an argument list. |This pattern matches any remaining elements in a sequence. |Consider the following example: | @@ -943,7 +943,7 @@ extends SyntaxMsg(IllegalStartOfSimplePatternID) { | case List(_, second, x:_*) => second | case _ => 0 |}""" - e"""|Simple patterns can be divided into several groups: + i"""|Simple patterns can be divided into several groups: |- Variable Patterns: ${hl("case x => ...")}. | It matches any value, and binds the variable name to that value. | A special case is the wild-card pattern _ which is treated as if it was a fresh @@ -998,17 +998,17 @@ extends SyntaxMsg(IllegalStartOfSimplePatternID) { class PkgDuplicateSymbol(existing: Symbol)(using Context) extends NamingMsg(PkgDuplicateSymbolID) { - def msg(using Context) = e"Trying to define package with same name as $existing" + def msg(using Context) = i"Trying to define package with same name as $existing" def explain(using Context) = "" } class ExistentialTypesNoLongerSupported()(using Context) extends SyntaxMsg(ExistentialTypesNoLongerSupportedID) { def msg(using Context) = - e"""|Existential types are no longer supported - + i"""|Existential types are no longer supported - |use a wildcard or dependent type instead""" def explain(using Context) = - e"""|The use of existential types is no longer supported. + i"""|The use of existential types is no longer supported. | |You should use a wildcard or dependent type instead. | @@ -1028,7 +1028,7 @@ class UnboundWildcardType()(using Context) extends SyntaxMsg(UnboundWildcardTypeID) { def msg(using Context) = "Unbound wildcard type" def explain(using Context) = - e"""|The wildcard type syntax (${hl("_")}) was used where it could not be bound. + i"""|The wildcard type syntax (${hl("_")}) was used where it could not be bound. |Replace ${hl("_")} with a non-wildcard type. If the type doesn't matter, |try replacing ${hl("_")} with ${hl("Any")}. | @@ -1070,10 +1070,10 @@ extends SyntaxMsg(UnboundWildcardTypeID) { class OverridesNothing(member: Symbol)(using Context) extends DeclarationMsg(OverridesNothingID) { - def msg(using Context) = e"""${member} overrides nothing""" + def msg(using Context) = i"""${member} overrides nothing""" def explain(using Context) = - e"""|There must be a field or method with the name ${member.name} in a super + i"""|There must be a field or method with the name ${member.name} in a super |class of ${member.owner} to override it. Did you misspell it? |Are you extending the right classes? |""" @@ -1086,10 +1086,10 @@ extends DeclarationMsg(OverridesNothingButNameExistsID) { if !existing.exists(_.symbol.hasTargetName(member.targetName)) then "target name" else "signature" - e"""${member} has a different $what than the overridden declaration""" + i"""${member} has a different $what than the overridden declaration""" def explain(using Context) = val existingDecl: String = existing.map(_.showDcl).mkString(" \n") - e"""|There must be a non-final field or method with the name ${member.name} and the + i"""|There must be a non-final field or method with the name ${member.name} and the |same parameter list in a super class of ${member.owner} to override it. | | ${member.showDcl} @@ -1123,10 +1123,10 @@ extends DeclarationMsg(OverrideErrorID), NoDisambiguation: class ForwardReferenceExtendsOverDefinition(value: Symbol, definition: Symbol)(using Context) extends ReferenceMsg(ForwardReferenceExtendsOverDefinitionID) { - def msg(using Context) = e"${definition.name} is a forward reference extending over the definition of ${value.name}" + def msg(using Context) = i"${definition.name} is a forward reference extending over the definition of ${value.name}" def explain(using Context) = - e"""|${definition.name} is used before you define it, and the definition of ${value.name} + i"""|${definition.name} is used before you define it, and the definition of ${value.name} |appears between that use and the definition of ${definition.name}. | |Forward references are allowed only, if there are no value definitions between @@ -1148,7 +1148,7 @@ extends SyntaxMsg(ExpectedTokenButFoundID) { val expectedText = if (Tokens.isIdentifier(expected)) "an identifier" else Tokens.showToken(expected) - e"""${expectedText} expected, but ${foundText} found""" + i"""${expectedText} expected, but ${foundText} found""" def explain(using Context) = if (Tokens.isIdentifier(expected) && Tokens.isKeyword(found)) @@ -1163,7 +1163,7 @@ extends SyntaxMsg(MixedLeftAndRightAssociativeOpsID) { def msg(using Context) = val op1Asso: String = if (op2LeftAssoc) "which is right-associative" else "which is left-associative" val op2Asso: String = if (op2LeftAssoc) "which is left-associative" else "which is right-associative" - e"${op1} (${op1Asso}) and ${op2} ($op2Asso) have same precedence and may not be mixed" + i"${op1} (${op1Asso}) and ${op2} ($op2Asso) have same precedence and may not be mixed" def explain(using Context) = s"""|The operators ${op1} and ${op2} are used as infix operators in the same expression, |but they bind to different sides: @@ -1195,9 +1195,9 @@ extends SyntaxMsg(MixedLeftAndRightAssociativeOpsID) { class CantInstantiateAbstractClassOrTrait(cls: Symbol, isTrait: Boolean)(using Context) extends TypeMsg(CantInstantiateAbstractClassOrTraitID) { private val traitOrAbstract = if (isTrait) "a trait" else "abstract" - def msg(using Context) = e"""${cls.name} is ${traitOrAbstract}; it cannot be instantiated""" + def msg(using Context) = i"""${cls.name} is ${traitOrAbstract}; it cannot be instantiated""" def explain(using Context) = - e"""|Abstract classes and traits need to be extended by a concrete class or object + i"""|Abstract classes and traits need to be extended by a concrete class or object |to make their functionality accessible. | |You may want to create an anonymous class extending ${cls.name} with @@ -1211,17 +1211,17 @@ extends TypeMsg(CantInstantiateAbstractClassOrTraitID) { } class UnreducibleApplication(tycon: Type)(using Context) extends TypeMsg(UnreducibleApplicationID): - def msg(using Context) = e"unreducible application of higher-kinded type $tycon to wildcard arguments" + def msg(using Context) = i"unreducible application of higher-kinded type $tycon to wildcard arguments" def explain(using Context) = - e"""|An abstract type constructor cannot be applied to wildcard arguments. + i"""|An abstract type constructor cannot be applied to wildcard arguments. |Such applications are equivalent to existential types, which are not |supported in Scala 3.""" class OverloadedOrRecursiveMethodNeedsResultType(cycleSym: Symbol)(using Context) extends CyclicMsg(OverloadedOrRecursiveMethodNeedsResultTypeID) { - def msg(using Context) = e"""Overloaded or recursive $cycleSym needs return type""" + def msg(using Context) = i"""Overloaded or recursive $cycleSym needs return type""" def explain(using Context) = - e"""Case 1: $cycleSym is overloaded + i"""Case 1: $cycleSym is overloaded |If there are multiple methods named $cycleSym and at least one definition of |it calls another, you need to specify the calling method's return type. | @@ -1233,9 +1233,9 @@ extends CyclicMsg(OverloadedOrRecursiveMethodNeedsResultTypeID) { class RecursiveValueNeedsResultType(cycleSym: Symbol)(using Context) extends CyclicMsg(RecursiveValueNeedsResultTypeID) { - def msg(using Context) = e"""Recursive $cycleSym needs type""" + def msg(using Context) = i"""Recursive $cycleSym needs type""" def explain(using Context) = - e"""The definition of $cycleSym is recursive and you need to specify its type. + i"""The definition of $cycleSym is recursive and you need to specify its type. |""" } @@ -1243,9 +1243,9 @@ class CyclicReferenceInvolving(denot: SymDenotation)(using Context) extends CyclicMsg(CyclicReferenceInvolvingID) { def msg(using Context) = val where = if denot.exists then s" involving $denot" else "" - e"Cyclic reference$where" + i"Cyclic reference$where" def explain(using Context) = - e"""|$denot is declared as part of a cycle which makes it impossible for the + i"""|$denot is declared as part of a cycle which makes it impossible for the |compiler to decide upon ${denot.name}'s type. |To avoid this error, try giving ${denot.name} an explicit type. |""" @@ -1253,9 +1253,9 @@ extends CyclicMsg(CyclicReferenceInvolvingID) { class CyclicReferenceInvolvingImplicit(cycleSym: Symbol)(using Context) extends CyclicMsg(CyclicReferenceInvolvingImplicitID) { - def msg(using Context) = e"""Cyclic reference involving implicit $cycleSym""" + def msg(using Context) = i"""Cyclic reference involving implicit $cycleSym""" def explain(using Context) = - e"""|$cycleSym is declared as part of a cycle which makes it impossible for the + i"""|$cycleSym is declared as part of a cycle which makes it impossible for the |compiler to decide upon ${cycleSym.name}'s type. |This might happen when the right hand-side of $cycleSym's definition involves an implicit search. |To avoid this error, try giving ${cycleSym.name} an explicit type. @@ -1268,14 +1268,14 @@ extends TypeMsg(SkolemInInferredID): def argStr = if argument.isEmpty then "" else i" from argument of type ${argument.tpe.widen}" - e"""Failure to generate given instance for type $pt$argStr) + i"""Failure to generate given instance for type $pt$argStr) | |I found: $tree |But the part corresponding to `` is not a reference that can be generated. |This might be because resolution yielded as given instance a function that is not |known to be total and side-effect free.""" def explain(using Context) = - e"""The part of given resolution that corresponds to `` produced a term that + i"""The part of given resolution that corresponds to `` produced a term that |is not a stable reference. Therefore a given instance could not be generated. | |To trouble-shoot the problem, try to supply an explicit expression instead of @@ -1283,10 +1283,10 @@ extends TypeMsg(SkolemInInferredID): class SuperQualMustBeParent(qual: untpd.Ident, cls: ClassSymbol)(using Context) extends ReferenceMsg(SuperQualMustBeParentID) { - def msg(using Context) = e"""|$qual does not name a parent of $cls""" + def msg(using Context) = i"""|$qual does not name a parent of $cls""" def explain(using Context) = val parents: Seq[String] = (cls.info.parents map (_.typeSymbol.name.show)).sorted - e"""|When a qualifier ${hl("T")} is used in a ${hl("super")} prefix of the form ${hl("C.super[T]")}, + i"""|When a qualifier ${hl("T")} is used in a ${hl("super")} prefix of the form ${hl("C.super[T]")}, |${hl("T")} must be a parent type of ${hl("C")}. | |In this case, the parents of $cls are: @@ -1296,9 +1296,9 @@ extends ReferenceMsg(SuperQualMustBeParentID) { class VarArgsParamMustComeLast()(using Context) extends SyntaxMsg(VarArgsParamMustComeLastID) { - def msg(using Context) = e"""${hl("varargs")} parameter must come last""" + def msg(using Context) = i"""${hl("varargs")} parameter must come last""" def explain(using Context) = - e"""|The ${hl("varargs")} field must be the last field in the method signature. + i"""|The ${hl("varargs")} field must be the last field in the method signature. |Attempting to define a field in a method signature after a ${hl("varargs")} field is an error. |""" } @@ -1322,18 +1322,18 @@ class AmbiguousReference(name: Name, newPrec: BindingPrec, prevPrec: BindingPrec case BindingPrec.NothingBound => assert(false) } if (prec.isImportPrec) { - ex"""$howVisible$qualifier by ${e"${whereFound.importInfo}"}""" + i"""$howVisible$qualifier by ${whereFound.importInfo}""" } else - ex"""$howVisible$qualifier in ${e"${whereFound.owner}"}""" + i"""$howVisible$qualifier in ${whereFound.owner}""" } def msg(using Context) = - i"""|Reference to ${e"$name"} is ambiguous, + i"""|Reference to $name is ambiguous, |it is both ${bindingString(newPrec, ctx)} |and ${bindingString(prevPrec, prevCtx, " subsequently")}""" def explain(using Context) = - e"""|The compiler can't decide which of the possible choices you + i"""|The compiler can't decide which of the possible choices you |are referencing with $name: A definition of lower precedence |in an inner scope, or a definition with higher precedence in |an outer scope. @@ -1362,7 +1362,7 @@ extends TypeMsg(MethodDoesNotTakeParametersId) { val more = if (tree.isInstanceOf[tpd.Apply]) " more" else "" val meth = methodSymbol val methStr = if (meth.exists) meth.showLocated else "expression" - e"$methStr does not take$more parameters" + i"$methStr does not take$more parameters" } def explain(using Context) = { @@ -1381,10 +1381,10 @@ class AmbiguousOverload(tree: tpd.Tree, val alternatives: List[SingleDenotation] extends ReferenceMsg(AmbiguousOverloadID), NoDisambiguation { private def all = if (alternatives.length == 2) "both" else "all" def msg(using Context) = - e"""|Ambiguous overload. The ${err.overloadedAltsStr(alternatives)} + i"""|Ambiguous overload. The ${err.overloadedAltsStr(alternatives)} |$all match ${err.expectedTypeStr(pt)}$addendum""" def explain(using Context) = - e"""|There are ${alternatives.length} methods that could be referenced as the compiler knows too little + i"""|There are ${alternatives.length} methods that could be referenced as the compiler knows too little |about the expected type. |You may specify the expected type e.g. by |- assigning it to a value with a specified type, or @@ -1394,9 +1394,9 @@ extends ReferenceMsg(AmbiguousOverloadID), NoDisambiguation { class ReassignmentToVal(name: Name)(using Context) extends TypeMsg(ReassignmentToValID) { - def msg(using Context) = e"""Reassignment to val $name""" + def msg(using Context) = i"""Reassignment to val $name""" def explain(using Context) = - e"""|You can not assign a new value to $name as values can't be changed. + i"""|You can not assign a new value to $name as values can't be changed. |Keep in mind that every statement has a value, so you may e.g. use | ${hl("val")} $name ${hl("= if (condition) 2 else 5")} |In case you need a reassignable name, you can declare it as @@ -1411,22 +1411,22 @@ class TypeDoesNotTakeParameters(tpe: Type, params: List[untpd.Tree])(using Conte if tpe.typeSymbol.isAllOf(Provisional | TypeParam) then "\n(Note that F-bounds of type parameters may not be type lambdas)" else "" - def msg(using Context) = e"$tpe does not take type parameters$fboundsAddendum" + def msg(using Context) = i"$tpe does not take type parameters$fboundsAddendum" def explain(using Context) = val ps = if (params.size == 1) s"a type parameter ${params.head}" else s"type parameters ${params.map(_.show).mkString(", ")}" - i"""You specified ${NoColor(ps)} for ${e"$tpe"}, which is not + i"""You specified ${NoColor(ps)} for $tpe, which is not |declared to take any. |""" } class VarValParametersMayNotBeCallByName(name: TermName, mutable: Boolean)(using Context) extends SyntaxMsg(VarValParametersMayNotBeCallByNameID) { - def varOrVal = if (mutable) e"${hl("var")}" else e"${hl("val")}" + def varOrVal = if mutable then hl("var") else hl("val") def msg(using Context) = s"$varOrVal parameters may not be call-by-name" def explain(using Context) = - e"""${hl("var")} and ${hl("val")} parameters of classes and traits may no be call-by-name. In case you + i"""${hl("var")} and ${hl("val")} parameters of classes and traits may no be call-by-name. In case you |want the parameter to be evaluated on demand, consider making it just a parameter |and a ${hl("def")} in the class such as | ${s"class MyClass(${name}Tick: => String) {"} @@ -1438,8 +1438,9 @@ class VarValParametersMayNotBeCallByName(name: TermName, mutable: Boolean)(using class MissingTypeParameterFor(tpe: Type)(using Context) extends SyntaxMsg(MissingTypeParameterForID) { def msg(using Context) = - if (tpe.derivesFrom(defn.AnyKindClass)) e"${tpe} cannot be used as a value type" - else e"Missing type parameter for ${tpe}" + if tpe.derivesFrom(defn.AnyKindClass) + then i"$tpe cannot be used as a value type" + else i"Missing type parameter for $tpe" def explain(using Context) = "" } @@ -1447,8 +1448,8 @@ class MissingTypeParameterInTypeApp(tpe: Type)(using Context) extends TypeMsg(MissingTypeParameterInTypeAppID) { def numParams = tpe.typeParams.length def parameters = if (numParams == 1) "parameter" else "parameters" - def msg(using Context) = e"Missing type $parameters for $tpe" - def explain(using Context) = e"A fully applied type is expected but $tpe takes $numParams $parameters" + def msg(using Context) = i"Missing type $parameters for $tpe" + def explain(using Context) = i"A fully applied type is expected but $tpe takes $numParams $parameters" } class MissingArgument(pname: Name, methString: String)(using Context) @@ -1468,28 +1469,28 @@ class DoesNotConformToBound(tpe: Type, which: String, bound: Type)(using Context override def canExplain = !isBounds def msg(using Context) = if isBounds then - e"Type argument ${tpe} does not overlap with $which bound $bound" + i"Type argument ${tpe} does not overlap with $which bound $bound" else - e"Type argument ${tpe} does not conform to $which bound $bound" + i"Type argument ${tpe} does not conform to $which bound $bound" class DoesNotConformToSelfType(category: String, selfType: Type, cls: Symbol, otherSelf: Type, relation: String, other: Symbol)( implicit ctx: Context) extends TypeMismatchMsg(selfType, otherSelf)(DoesNotConformToSelfTypeID) { - def msg(using Context) = e"""$category: self type $selfType of $cls does not conform to self type $otherSelf + def msg(using Context) = i"""$category: self type $selfType of $cls does not conform to self type $otherSelf |of $relation $other""" } class DoesNotConformToSelfTypeCantBeInstantiated(tp: Type, selfType: Type)( implicit ctx: Context) extends TypeMismatchMsg(tp, selfType)(DoesNotConformToSelfTypeCantBeInstantiatedID) { - def msg(using Context) = e"""$tp does not conform to its self type $selfType; cannot be instantiated""" + def msg(using Context) = i"""$tp does not conform to its self type $selfType; cannot be instantiated""" } class IllegalParameterInit(found: Type, expected: Type, param: Symbol, cls: Symbol)(using Context) extends TypeMismatchMsg(found, expected)(IllegalParameterInitID): def msg(using Context) = - e"""illegal parameter initialization of $param. + i"""illegal parameter initialization of $param. | | The argument passed for $param has type: $found | but $cls expects $param to have type: $expected""" @@ -1497,13 +1498,13 @@ class IllegalParameterInit(found: Type, expected: Type, param: Symbol, cls: Symb class AbstractMemberMayNotHaveModifier(sym: Symbol, flag: FlagSet)( implicit ctx: Context) extends SyntaxMsg(AbstractMemberMayNotHaveModifierID) { - def msg(using Context) = e"""${hl("abstract")} $sym may not have `${flag.flagsString}` modifier""" + def msg(using Context) = i"""${hl("abstract")} $sym may not have `${flag.flagsString}` modifier""" def explain(using Context) = "" } class TypesAndTraitsCantBeImplicit()(using Context) extends SyntaxMsg(TypesAndTraitsCantBeImplicitID) { - def msg(using Context) = e"""${hl("implicit")} modifier cannot be used for types or traits""" + def msg(using Context) = i"""${hl("implicit")} modifier cannot be used for types or traits""" def explain(using Context) = "" } @@ -1511,20 +1512,20 @@ class OnlyClassesCanBeAbstract(sym: Symbol)( implicit ctx: Context) extends SyntaxMsg(OnlyClassesCanBeAbstractID) { def explain(using Context) = "" - def msg(using Context) = e"""${hl("abstract")} modifier can be used only for classes; it should be omitted for abstract members""" + def msg(using Context) = i"""${hl("abstract")} modifier can be used only for classes; it should be omitted for abstract members""" } class AbstractOverrideOnlyInTraits(sym: Symbol)( implicit ctx: Context) extends SyntaxMsg(AbstractOverrideOnlyInTraitsID) { - def msg(using Context) = e"""${hl("abstract override")} modifier only allowed for members of traits""" + def msg(using Context) = i"""${hl("abstract override")} modifier only allowed for members of traits""" def explain(using Context) = "" } class TraitsMayNotBeFinal(sym: Symbol)( implicit ctx: Context) extends SyntaxMsg(TraitsMayNotBeFinalID) { - def msg(using Context) = e"""$sym may not be ${hl("final")}""" + def msg(using Context) = i"""$sym may not be ${hl("final")}""" def explain(using Context) = "A trait can never be final since it is abstract and must be extended to be useful." } @@ -1532,14 +1533,14 @@ class TraitsMayNotBeFinal(sym: Symbol)( class NativeMembersMayNotHaveImplementation(sym: Symbol)( implicit ctx: Context) extends SyntaxMsg(NativeMembersMayNotHaveImplementationID) { - def msg(using Context) = e"""${hl("@native")} members may not have an implementation""" + def msg(using Context) = i"""${hl("@native")} members may not have an implementation""" def explain(using Context) = "" } class TraitMayNotDefineNativeMethod(sym: Symbol)( implicit ctx: Context) extends SyntaxMsg(TraitMayNotDefineNativeMethodID) { - def msg(using Context) = e"""A trait cannot define a ${hl("@native")} method.""" + def msg(using Context) = i"""A trait cannot define a ${hl("@native")} method.""" def explain(using Context) = "" } @@ -1547,7 +1548,7 @@ class OnlyClassesCanHaveDeclaredButUndefinedMembers(sym: Symbol)( implicit ctx: Context) extends SyntaxMsg(OnlyClassesCanHaveDeclaredButUndefinedMembersID) { - def msg(using Context) = e"""Declaration of $sym not allowed here: only classes can have declared but undefined members""" + def msg(using Context) = i"""Declaration of $sym not allowed here: only classes can have declared but undefined members""" def explain(using Context) = if sym.is(Mutable) then "Note that variables need to be initialized to be defined." else "" @@ -1555,9 +1556,9 @@ class OnlyClassesCanHaveDeclaredButUndefinedMembers(sym: Symbol)( class CannotExtendAnyVal(sym: Symbol)(using Context) extends SyntaxMsg(CannotExtendAnyValID) { - def msg(using Context) = e"""$sym cannot extend ${hl("AnyVal")}""" + def msg(using Context) = i"""$sym cannot extend ${hl("AnyVal")}""" def explain(using Context) = - e"""Only classes (not traits) are allowed to extend ${hl("AnyVal")}, but traits may extend + i"""Only classes (not traits) are allowed to extend ${hl("AnyVal")}, but traits may extend |${hl("Any")} to become ${Green("\"universal traits\"")} which may only have ${hl("def")} members. |Universal traits can be mixed into classes that extend ${hl("AnyVal")}. |""" @@ -1565,19 +1566,19 @@ class CannotExtendAnyVal(sym: Symbol)(using Context) class CannotExtendJavaEnum(sym: Symbol)(using Context) extends SyntaxMsg(CannotExtendJavaEnumID) { - def msg(using Context) = e"""$sym cannot extend ${hl("java.lang.Enum")}: only enums defined with the ${hl("enum")} syntax can""" + def msg(using Context) = i"""$sym cannot extend ${hl("java.lang.Enum")}: only enums defined with the ${hl("enum")} syntax can""" def explain(using Context) = "" } class CannotExtendContextFunction(sym: Symbol)(using Context) extends SyntaxMsg(CannotExtendFunctionID) { - def msg(using Context) = e"""$sym cannot extend a context function class""" + def msg(using Context) = i"""$sym cannot extend a context function class""" def explain(using Context) = "" } class JavaEnumParentArgs(parent: Type)(using Context) extends TypeMsg(JavaEnumParentArgsID) { - def msg(using Context) = e"""not enough arguments for constructor Enum: ${hl("(name: String, ordinal: Int)")}: ${hl(parent.show)}""" + def msg(using Context) = i"""not enough arguments for constructor Enum: ${hl("(name: String, ordinal: Int)")}: ${hl(parent.show)}""" def explain(using Context) = "" } @@ -1592,7 +1593,7 @@ class CannotHaveSameNameAs(sym: Symbol, cls: Symbol, reason: CannotHaveSameNameA |""".stripMargin } - def msg(using Context) = e"""$sym cannot have the same name as ${cls.showLocated} -- """ + reasonMessage + def msg(using Context) = i"""$sym cannot have the same name as ${cls.showLocated} -- """ + reasonMessage def explain(using Context) = "" } object CannotHaveSameNameAs { @@ -1603,31 +1604,31 @@ object CannotHaveSameNameAs { class ValueClassesMayNotDefineInner(valueClass: Symbol, inner: Symbol)(using Context) extends SyntaxMsg(ValueClassesMayNotDefineInnerID) { - def msg(using Context) = e"""Value classes may not define an inner class""" + def msg(using Context) = i"""Value classes may not define an inner class""" def explain(using Context) = "" } class ValueClassesMayNotDefineNonParameterField(valueClass: Symbol, field: Symbol)(using Context) extends SyntaxMsg(ValueClassesMayNotDefineNonParameterFieldID) { - def msg(using Context) = e"""Value classes may not define non-parameter field""" + def msg(using Context) = i"""Value classes may not define non-parameter field""" def explain(using Context) = "" } class ValueClassesMayNotDefineASecondaryConstructor(valueClass: Symbol, constructor: Symbol)(using Context) extends SyntaxMsg(ValueClassesMayNotDefineASecondaryConstructorID) { - def msg(using Context) = e"""Value classes may not define a secondary constructor""" + def msg(using Context) = i"""Value classes may not define a secondary constructor""" def explain(using Context) = "" } class ValueClassesMayNotContainInitalization(valueClass: Symbol)(using Context) extends SyntaxMsg(ValueClassesMayNotContainInitalizationID) { - def msg(using Context) = e"""Value classes may not contain initialization statements""" + def msg(using Context) = i"""Value classes may not contain initialization statements""" def explain(using Context) = "" } class ValueClassesMayNotBeAbstract(valueClass: Symbol)(using Context) extends SyntaxMsg(ValueClassesMayNotBeAbstractID) { - def msg(using Context) = e"""Value classes may not be ${hl("abstract")}""" + def msg(using Context) = i"""Value classes may not be ${hl("abstract")}""" def explain(using Context) = "" } @@ -1646,14 +1647,14 @@ class ValueClassesMayNotWrapAnotherValueClass(valueClass: Symbol)(using Context) class ValueClassParameterMayNotBeAVar(valueClass: Symbol, param: Symbol)(using Context) extends SyntaxMsg(ValueClassParameterMayNotBeAVarID) { - def msg(using Context) = e"""A value class parameter may not be a ${hl("var")}""" + def msg(using Context) = i"""A value class parameter may not be a ${hl("var")}""" def explain(using Context) = - e"""A value class must have exactly one ${hl("val")} parameter.""" + i"""A value class must have exactly one ${hl("val")} parameter.""" } class ValueClassNeedsOneValParam(valueClass: Symbol)(using Context) extends SyntaxMsg(ValueClassNeedsExactlyOneValParamID) { - def msg(using Context) = e"""Value class needs one ${hl("val")} parameter""" + def msg(using Context) = i"""Value class needs one ${hl("val")} parameter""" def explain(using Context) = "" } @@ -1665,12 +1666,12 @@ class ValueClassParameterMayNotBeCallByName(valueClass: Symbol, param: Symbol)(u class SuperCallsNotAllowedInlineable(symbol: Symbol)(using Context) extends SyntaxMsg(SuperCallsNotAllowedInlineableID) { - def msg(using Context) = e"Super call not allowed in inlineable $symbol" + def msg(using Context) = i"Super call not allowed in inlineable $symbol" def explain(using Context) = "Method inlining prohibits calling superclass methods, as it may lead to confusion about which super is being called." } class NotAPath(tp: Type, usage: String)(using Context) extends TypeMsg(NotAPathID): - def msg(using Context) = e"$tp is not a valid $usage, since it is not an immutable path" + def msg(using Context) = i"$tp is not a valid $usage, since it is not an immutable path" def explain(using Context) = i"""An immutable path is | - a reference to an immutable value, or @@ -1687,21 +1688,21 @@ class DuplicatePrivateProtectedQualifier()(using Context) extends SyntaxMsg(DuplicatePrivateProtectedQualifierID) { def msg(using Context) = "Duplicate private/protected qualifier" def explain(using Context) = - e"It is not allowed to combine `private` and `protected` modifiers even if they are qualified to different scopes" + i"It is not allowed to combine `private` and `protected` modifiers even if they are qualified to different scopes" } class ExpectedStartOfTopLevelDefinition()(using Context) extends SyntaxMsg(ExpectedStartOfTopLevelDefinitionID) { def msg(using Context) = "Expected start of definition" def explain(using Context) = - e"You have to provide either ${hl("class")}, ${hl("trait")}, ${hl("object")}, or ${hl("enum")} definitions after qualifiers" + i"You have to provide either ${hl("class")}, ${hl("trait")}, ${hl("object")}, or ${hl("enum")} definitions after qualifiers" } class NoReturnFromInlineable(owner: Symbol)(using Context) extends SyntaxMsg(NoReturnFromInlineableID) { - def msg(using Context) = e"No explicit ${hl("return")} allowed from inlineable $owner" + def msg(using Context) = i"No explicit ${hl("return")} allowed from inlineable $owner" def explain(using Context) = - e"""Methods marked with ${hl("inline")} modifier may not use ${hl("return")} statements. + i"""Methods marked with ${hl("inline")} modifier may not use ${hl("return")} statements. |Instead, you should rely on the last expression's value being |returned from a method. |""" @@ -1709,26 +1710,26 @@ class NoReturnFromInlineable(owner: Symbol)(using Context) class ReturnOutsideMethodDefinition(owner: Symbol)(using Context) extends SyntaxMsg(ReturnOutsideMethodDefinitionID) { - def msg(using Context) = e"${hl("return")} outside method definition" + def msg(using Context) = i"${hl("return")} outside method definition" def explain(using Context) = - e"""You used ${hl("return")} in ${owner}. + i"""You used ${hl("return")} in ${owner}. |${hl("return")} is a keyword and may only be used within method declarations. |""" } class ExtendFinalClass(clazz:Symbol, finalClazz: Symbol)(using Context) extends SyntaxMsg(ExtendFinalClassID) { - def msg(using Context) = e"$clazz cannot extend ${hl("final")} $finalClazz" + def msg(using Context) = i"$clazz cannot extend ${hl("final")} $finalClazz" def explain(using Context) = - e"""A class marked with the ${hl("final")} keyword cannot be extended""" + i"""A class marked with the ${hl("final")} keyword cannot be extended""" } class ExpectedTypeBoundOrEquals(found: Token)(using Context) extends SyntaxMsg(ExpectedTypeBoundOrEqualsID) { - def msg(using Context) = e"${hl("=")}, ${hl(">:")}, or ${hl("<:")} expected, but ${Tokens.showToken(found)} found" + def msg(using Context) = i"${hl("=")}, ${hl(">:")}, or ${hl("<:")} expected, but ${Tokens.showToken(found)} found" def explain(using Context) = - e"""Type parameters and abstract types may be constrained by a type bound. + i"""Type parameters and abstract types may be constrained by a type bound. |Such type bounds limit the concrete values of the type variables and possibly |reveal more information about the members of such types. | @@ -1744,9 +1745,9 @@ class ClassAndCompanionNameClash(cls: Symbol, other: Symbol)(using Context) extends NamingMsg(ClassAndCompanionNameClashID) { def msg(using Context) = val name = cls.name.stripModuleClassSuffix - e"Name clash: both ${cls.owner} and its companion object defines $name" + i"Name clash: both ${cls.owner} and its companion object defines $name" def explain(using Context) = - e"""|A ${cls.kindString} and its companion object cannot both define a ${hl("class")}, ${hl("trait")} or ${hl("object")} with the same name: + i"""|A ${cls.kindString} and its companion object cannot both define a ${hl("class")}, ${hl("trait")} or ${hl("object")} with the same name: | - ${cls.owner} defines ${cls} | - ${other.owner} defines ${other}""" } @@ -1755,10 +1756,10 @@ class TailrecNotApplicable(symbol: Symbol)(using Context) extends SyntaxMsg(TailrecNotApplicableID) { def msg(using Context) = { val reason = - if (!symbol.is(Method)) e"$symbol isn't a method" - else if (symbol.is(Deferred)) e"$symbol is abstract" - else if (!symbol.isEffectivelyFinal) e"$symbol is neither ${hl("private")} nor ${hl("final")} so can be overridden" - else e"$symbol contains no recursive calls" + if !symbol.is(Method) then i"$symbol isn't a method" + else if symbol.is(Deferred) then i"$symbol is abstract" + else if !symbol.isEffectivelyFinal then i"$symbol is neither ${hl("private")} nor ${hl("final")} so can be overridden" + else i"$symbol contains no recursive calls" s"TailRec optimisation not applicable, $reason" } @@ -1770,7 +1771,7 @@ class FailureToEliminateExistential(tp: Type, tp1: Type, tp2: Type, boundSyms: L def kind = MessageKind.Compatibility def msg(using Context) = val originalType = ctx.printer.dclsText(boundSyms, "; ").show - e"""An existential type that came from a Scala-2 classfile for $classRoot + i"""An existential type that came from a Scala-2 classfile for $classRoot |cannot be mapped accurately to a Scala-3 equivalent. |original type : $tp forSome ${originalType} |reduces to : $tp1 @@ -1778,7 +1779,7 @@ class FailureToEliminateExistential(tp: Type, tp1: Type, tp2: Type, boundSyms: L |This choice can cause follow-on type errors or hide type errors. |Proceed at own risk.""" def explain(using Context) = - e"""Existential types in their full generality are no longer supported. + i"""Existential types in their full generality are no longer supported. |Scala-3 does applications of class types to wildcard type arguments. |Other forms of existential types that come from Scala-2 classfiles |are only approximated in a best-effort way.""" @@ -1786,21 +1787,21 @@ class FailureToEliminateExistential(tp: Type, tp1: Type, tp2: Type, boundSyms: L class OnlyFunctionsCanBeFollowedByUnderscore(tp: Type)(using Context) extends SyntaxMsg(OnlyFunctionsCanBeFollowedByUnderscoreID) { - def msg(using Context) = e"Only function types can be followed by ${hl("_")} but the current expression has type $tp" + def msg(using Context) = i"Only function types can be followed by ${hl("_")} but the current expression has type $tp" def explain(using Context) = - e"""The syntax ${hl("x _")} is no longer supported if ${hl("x")} is not a function. + i"""The syntax ${hl("x _")} is no longer supported if ${hl("x")} is not a function. |To convert to a function value, you need to explicitly write ${hl("() => x")}""" } class MissingEmptyArgumentList(method: String)(using Context) extends SyntaxMsg(MissingEmptyArgumentListID) { - def msg(using Context) = e"$method must be called with ${hl("()")} argument" + def msg(using Context) = i"$method must be called with ${hl("()")} argument" def explain(using Context) = { val codeExample = """def next(): T = ... |next // is expanded to next()""" - e"""Previously an empty argument list () was implicitly inserted when calling a nullary method without arguments. E.g. + i"""Previously an empty argument list () was implicitly inserted when calling a nullary method without arguments. E.g. | |$codeExample | @@ -1811,13 +1812,13 @@ class MissingEmptyArgumentList(method: String)(using Context) class DuplicateNamedTypeParameter(name: Name)(using Context) extends SyntaxMsg(DuplicateNamedTypeParameterID) { - def msg(using Context) = e"Type parameter $name was defined multiple times." + def msg(using Context) = i"Type parameter $name was defined multiple times." def explain(using Context) = "" } class UndefinedNamedTypeParameter(undefinedName: Name, definedNames: List[Name])(using Context) extends SyntaxMsg(UndefinedNamedTypeParameterID) { - def msg(using Context) = e"Type parameter $undefinedName is undefined. Expected one of ${definedNames.map(_.show).mkString(", ")}." + def msg(using Context) = i"Type parameter $undefinedName is undefined. Expected one of ${definedNames.map(_.show).mkString(", ")}." def explain(using Context) = "" } @@ -1834,7 +1835,7 @@ class IllegalStartOfStatement(what: String, isModifier: Boolean, isStat: Boolean } class TraitIsExpected(symbol: Symbol)(using Context) extends SyntaxMsg(TraitIsExpectedID) { - def msg(using Context) = e"$symbol is not a trait" + def msg(using Context) = i"$symbol is not a trait" def explain(using Context) = { val errorCodeExample = """class A @@ -1847,7 +1848,7 @@ class TraitIsExpected(symbol: Symbol)(using Context) extends SyntaxMsg(TraitIsEx | |val a = new A with B // compiles normally""".stripMargin - e"""Only traits can be mixed into classes using a ${hl("with")} keyword. + i"""Only traits can be mixed into classes using a ${hl("with")} keyword. |Consider the following example: | |$errorCodeExample @@ -1861,7 +1862,7 @@ class TraitIsExpected(symbol: Symbol)(using Context) extends SyntaxMsg(TraitIsEx } class TraitRedefinedFinalMethodFromAnyRef(method: Symbol)(using Context) extends SyntaxMsg(TraitRedefinedFinalMethodFromAnyRefID) { - def msg(using Context) = e"Traits cannot redefine final $method from ${hl("class AnyRef")}." + def msg(using Context) = i"Traits cannot redefine final $method from ${hl("class AnyRef")}." def explain(using Context) = "" } @@ -1878,27 +1879,27 @@ extends NamingMsg(AlreadyDefinedID): "\n\nNote that overloaded methods must all be defined in the same group of toplevel definitions" else "" if conflicting.isTerm != name.isTermName then - e"$name clashes with $conflicting$where; the two must be defined together" + i"$name clashes with $conflicting$where; the two must be defined together" else - e"$name is already defined as $conflicting$where$note" + i"$name is already defined as $conflicting$where$note" def explain(using Context) = "" class PackageNameAlreadyDefined(pkg: Symbol)(using Context) extends NamingMsg(PackageNameAlreadyDefinedID) { def msg(using Context) = def where = if pkg.associatedFile == null then "" else s" in ${pkg.associatedFile}" - e"""${pkg.name} is the name of $pkg$where. + i"""${pkg.name} is the name of $pkg$where. |It cannot be used at the same time as the name of a package.""" def explain(using Context) = def or = if pkg.associatedFile == null then "" else " or delete the containing class file" - e"""An ${hl("object")} or other toplevel definition cannot have the same name as an existing ${hl("package")}. + i"""An ${hl("object")} or other toplevel definition cannot have the same name as an existing ${hl("package")}. |Rename either one of them$or.""" } class UnapplyInvalidNumberOfArguments(qual: untpd.Tree, argTypes: List[Type])(using Context) extends SyntaxMsg(UnapplyInvalidNumberOfArgumentsID) { - def msg(using Context) = e"Wrong number of argument patterns for $qual; expected: ($argTypes%, %)" + def msg(using Context) = i"Wrong number of argument patterns for $qual; expected: ($argTypes%, %)" def explain(using Context) = - e"""The Unapply method of $qual was used with incorrect number of arguments. + i"""The Unapply method of $qual was used with incorrect number of arguments. |Expected usage would be something like: |case $qual(${argTypes.map(_ => '_')}%, %) => ... | @@ -1913,9 +1914,9 @@ class UnapplyInvalidReturnType(unapplyResult: Type, unapplyName: Name)(using Con if Feature.migrateTo3 && unapplyName == nme.unapplySeq then "\nYou might want to try to rewrite the extractor to use `unapply` instead." else "" - e"""| ${Red(i"$unapplyResult")} is not a valid result type of an $unapplyName method of an ${Magenta("extractor")}.$addendum""" + i"""| ${Red(i"$unapplyResult")} is not a valid result type of an $unapplyName method of an ${Magenta("extractor")}.$addendum""" def explain(using Context) = if (unapplyName.show == "unapply") - e""" + i""" |To be used as an extractor, an unapply method has to return a type that either: | - has members ${Magenta("isEmpty: Boolean")} and ${Magenta("get: S")} (usually an ${Green("Option[S]")}) | - is a ${Green("Boolean")} @@ -1944,7 +1945,7 @@ class UnapplyInvalidReturnType(unapplyResult: Type, unapplyName: Name)(using Con |} """ else - e""" + i""" |To be used as an extractor, an unapplySeq method has to return a type which has members |${Magenta("isEmpty: Boolean")} and ${Magenta("get: S")} where ${Magenta("S <: Seq[V]")} (usually an ${Green("Option[Seq[V]]")}): | @@ -1962,13 +1963,13 @@ class UnapplyInvalidReturnType(unapplyResult: Type, unapplyName: Name)(using Con } class StaticFieldsOnlyAllowedInObjects(member: Symbol)(using Context) extends SyntaxMsg(StaticFieldsOnlyAllowedInObjectsID) { - def msg(using Context) = e"${hl("@static")} $member in ${member.owner} must be defined inside a static ${hl("object")}." + def msg(using Context) = i"${hl("@static")} $member in ${member.owner} must be defined inside a static ${hl("object")}." def explain(using Context) = - e"${hl("@static")} members are only allowed inside objects." + i"${hl("@static")} members are only allowed inside objects." } class StaticFieldsShouldPrecedeNonStatic(member: Symbol, defns: List[tpd.Tree])(using Context) extends SyntaxMsg(StaticFieldsShouldPrecedeNonStaticID) { - def msg(using Context) = e"${hl("@static")} $member in ${member.owner} must be defined before non-static fields." + def msg(using Context) = i"${hl("@static")} $member in ${member.owner} must be defined before non-static fields." def explain(using Context) = { val nonStatics = defns.takeWhile(_.symbol != member).take(3).filter(_.isInstanceOf[tpd.ValDef]) val codeExample = s"""object ${member.owner.name.firstPart} { @@ -1976,7 +1977,7 @@ class StaticFieldsShouldPrecedeNonStatic(member: Symbol, defns: List[tpd.Tree])( | ${nonStatics.map(m => s"${m.symbol} = ...").mkString("\n ")} | ... |}""" - e"""The fields annotated with @static should precede any non @static fields. + i"""The fields annotated with @static should precede any non @static fields. |This ensures that we do not introduce surprises for users in initialization order of this class. |Static field are initialized when class loading the code of Foo. |Non static fields are only initialized the first time that Foo is accessed. @@ -1988,11 +1989,11 @@ class StaticFieldsShouldPrecedeNonStatic(member: Symbol, defns: List[tpd.Tree])( } class CyclicInheritance(symbol: Symbol, addendum: => String)(using Context) extends SyntaxMsg(CyclicInheritanceID) { - def msg(using Context) = e"Cyclic inheritance: $symbol extends itself$addendum" + def msg(using Context) = i"Cyclic inheritance: $symbol extends itself$addendum" def explain(using Context) = { val codeExample = "class A extends A" - e"""Cyclic inheritance is prohibited in Dotty. + i"""Cyclic inheritance is prohibited in Dotty. |Consider the following example: | |$codeExample @@ -2013,7 +2014,7 @@ extends ReferenceMsg(BadSymbolicReferenceID) { if (file != null) (s" in $file", file.toString) else ("", "the signature") - e"""Bad symbolic reference. A signature$location + i"""Bad symbolic reference. A signature$location |refers to $denotationName in ${denotationOwner.showKind} ${denotationOwner.showFullName} which is not available. |It may be completely missing from the current classpath, or the version on |the classpath might be incompatible with the version used when compiling $src.""" @@ -2023,15 +2024,15 @@ extends ReferenceMsg(BadSymbolicReferenceID) { } class UnableToExtendSealedClass(pclazz: Symbol)(using Context) extends SyntaxMsg(UnableToExtendSealedClassID) { - def msg(using Context) = e"Cannot extend ${hl("sealed")} $pclazz in a different source file" + def msg(using Context) = i"Cannot extend ${hl("sealed")} $pclazz in a different source file" def explain(using Context) = "A sealed class or trait can only be extended in the same file as its declaration" } class SymbolHasUnparsableVersionNumber(symbol: Symbol, errorMessage: String)(using Context) extends SyntaxMsg(SymbolHasUnparsableVersionNumberID) { - def msg(using Context) = e"${symbol.showLocated} has an unparsable version number: $errorMessage" + def msg(using Context) = i"${symbol.showLocated} has an unparsable version number: $errorMessage" def explain(using Context) = - e"""The ${symbol.showLocated} is marked with ${hl("@migration")} indicating it has changed semantics + i"""The ${symbol.showLocated} is marked with ${hl("@migration")} indicating it has changed semantics |between versions and the ${hl("-Xmigration")} settings is used to warn about constructs |whose behavior may have changed since version change.""" } @@ -2041,16 +2042,16 @@ class SymbolChangedSemanticsInVersion( migrationVersion: ScalaVersion, migrationMessage: String )(using Context) extends SyntaxMsg(SymbolChangedSemanticsInVersionID) { - def msg(using Context) = e"${symbol.showLocated} has changed semantics in version $migrationVersion: $migrationMessage" + def msg(using Context) = i"${symbol.showLocated} has changed semantics in version $migrationVersion: $migrationMessage" def explain(using Context) = - e"""The ${symbol.showLocated} is marked with ${hl("@migration")} indicating it has changed semantics + i"""The ${symbol.showLocated} is marked with ${hl("@migration")} indicating it has changed semantics |between versions and the ${hl("-Xmigration")} settings is used to warn about constructs |whose behavior may have changed since version change.""" } class UnableToEmitSwitch()(using Context) extends SyntaxMsg(UnableToEmitSwitchID) { - def msg(using Context) = e"Could not emit switch for ${hl("@switch")} annotated match" + def msg(using Context) = i"Could not emit switch for ${hl("@switch")} annotated match" def explain(using Context) = { val codeExample = """val ConstantB = 'B' @@ -2063,7 +2064,7 @@ extends SyntaxMsg(UnableToEmitSwitchID) { | case _ => 5 |}""".stripMargin - e"""If annotated with ${hl("@switch")}, the compiler will verify that the match has been compiled to a + i"""If annotated with ${hl("@switch")}, the compiler will verify that the match has been compiled to a |tableswitch or lookupswitch and issue an error if it instead compiles into a series of conditional |expressions. Example usage: | @@ -2078,34 +2079,32 @@ extends SyntaxMsg(UnableToEmitSwitchID) { class MissingCompanionForStatic(member: Symbol)(using Context) extends SyntaxMsg(MissingCompanionForStaticID) { - def msg(using Context) = e"${member.owner} does not have a companion class" + def msg(using Context) = i"${member.owner} does not have a companion class" def explain(using Context) = - e"An object that contains ${hl("@static")} members must have a companion class." + i"An object that contains ${hl("@static")} members must have a companion class." } class PolymorphicMethodMissingTypeInParent(rsym: Symbol, parentSym: Symbol)(using Context) extends SyntaxMsg(PolymorphicMethodMissingTypeInParentID) { - def msg(using Context) = e"Polymorphic refinement $rsym without matching type in parent $parentSym is no longer allowed" + def msg(using Context) = i"Polymorphic refinement $rsym without matching type in parent $parentSym is no longer allowed" def explain(using Context) = - e"""Polymorphic $rsym is not allowed in the structural refinement of $parentSym because + i"""Polymorphic $rsym is not allowed in the structural refinement of $parentSym because |$rsym does not override any method in $parentSym. Structural refinement does not allow for |polymorphic methods.""" } class ParamsNoInline(owner: Symbol)(using Context) extends SyntaxMsg(ParamsNoInlineID) { - def msg(using Context) = e"""${hl("inline")} modifier can only be used for parameters of inline methods""" + def msg(using Context) = i"""${hl("inline")} modifier can only be used for parameters of inline methods""" def explain(using Context) = "" } class JavaSymbolIsNotAValue(symbol: Symbol)(using Context) extends TypeMsg(JavaSymbolIsNotAValueID) { - def msg(using Context) = { + def msg(using Context) = val kind = - if (symbol is Package) e"$symbol" - else e"Java defined ${hl("class " + symbol.name)}" - + if symbol is Package then i"$symbol" + else i"Java defined ${hl("class " + symbol.name)}" s"$kind is not a value" - } def explain(using Context) = "" } @@ -2161,7 +2160,7 @@ extends NamingMsg(DoubleDefinitionID) { "Name clash between inherited members" atPhase(typerPhase) { - e"""$clashDescription: + i"""$clashDescription: |${previousDecl.showDcl} ${symLocation(previousDecl)} and |${decl.showDcl} ${symLocation(decl)} |""" @@ -2184,22 +2183,22 @@ class TypeTestAlwaysDiverges(scrutTp: Type, testTp: Type)(using Context) extends // Relative of CyclicReferenceInvolvingImplicit and RecursiveValueNeedsResultType class TermMemberNeedsResultTypeForImplicitSearch(cycleSym: Symbol)(using Context) extends CyclicMsg(TermMemberNeedsNeedsResultTypeForImplicitSearchID) { - def msg(using Context) = e"""$cycleSym needs result type because its right-hand side attempts implicit search""" + def msg(using Context) = i"""$cycleSym needs result type because its right-hand side attempts implicit search""" def explain(using Context) = - e"""|The right hand-side of $cycleSym's definition requires an implicit search at the highlighted position. + i"""|The right hand-side of $cycleSym's definition requires an implicit search at the highlighted position. |To avoid this error, give `$cycleSym` an explicit type. |""" } class ClassCannotExtendEnum(cls: Symbol, parent: Symbol)(using Context) extends SyntaxMsg(ClassCannotExtendEnumID) { - def msg(using Context) = e"""$cls in ${cls.owner} extends enum ${parent.name}, but extending enums is prohibited.""" + def msg(using Context) = i"""$cls in ${cls.owner} extends enum ${parent.name}, but extending enums is prohibited.""" def explain(using Context) = "" } class NotAnExtractor(tree: untpd.Tree)(using Context) extends SyntaxMsg(NotAnExtractorID) { - def msg(using Context) = e"$tree cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method" + def msg(using Context) = i"$tree cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method" def explain(using Context) = - e"""|An ${hl("unapply")} method should be defined in an ${hl("object")} as follow: + i"""|An ${hl("unapply")} method should be defined in an ${hl("object")} as follow: | - If it is just a test, return a ${hl("Boolean")}. For example ${hl("case even()")} | - If it returns a single sub-value of type T, return an ${hl("Option[T]")} | - If it returns several sub-values T1,...,Tn, group them in an optional tuple ${hl("Option[(T1,...,Tn)]")} @@ -2211,7 +2210,7 @@ class NotAnExtractor(tree: untpd.Tree)(using Context) extends SyntaxMsg(NotAnExt class MemberWithSameNameAsStatic()(using Context) extends SyntaxMsg(MemberWithSameNameAsStaticID) { - def msg(using Context) = e"Companion classes cannot define members with same name as a ${hl("@static")} member" + def msg(using Context) = i"Companion classes cannot define members with same name as a ${hl("@static")} member" def explain(using Context) = "" } @@ -2220,25 +2219,25 @@ class PureExpressionInStatementPosition(stat: untpd.Tree, val exprOwner: Symbol) def kind = MessageKind.PotentialIssue def msg(using Context) = "A pure expression does nothing in statement position; you may be omitting necessary parentheses" def explain(using Context) = - e"""The pure expression $stat doesn't have any side effect and its result is not assigned elsewhere. + i"""The pure expression $stat doesn't have any side effect and its result is not assigned elsewhere. |It can be removed without changing the semantics of the program. This may indicate an error.""" } class TraitCompanionWithMutableStatic()(using Context) extends SyntaxMsg(TraitCompanionWithMutableStaticID) { - def msg(using Context) = e"Companion of traits cannot define mutable @static fields" + def msg(using Context) = i"Companion of traits cannot define mutable @static fields" def explain(using Context) = "" } class LazyStaticField()(using Context) extends SyntaxMsg(LazyStaticFieldID) { - def msg(using Context) = e"Lazy @static fields are not supported" + def msg(using Context) = i"Lazy @static fields are not supported" def explain(using Context) = "" } class StaticOverridingNonStaticMembers()(using Context) extends SyntaxMsg(StaticOverridingNonStaticMembersID) { - def msg(using Context) = e"${hl("@static")} members cannot override or implement non-static ones" + def msg(using Context) = i"${hl("@static")} members cannot override or implement non-static ones" def explain(using Context) = "" } @@ -2246,21 +2245,21 @@ class OverloadInRefinement(rsym: Symbol)(using Context) extends DeclarationMsg(OverloadInRefinementID) { def msg(using Context) = "Refinements cannot introduce overloaded definitions" def explain(using Context) = - e"""The refinement `$rsym` introduces an overloaded definition. + i"""The refinement `$rsym` introduces an overloaded definition. |Refinements cannot contain overloaded definitions.""" } class NoMatchingOverload(val alternatives: List[SingleDenotation], pt: Type)(using Context) extends TypeMsg(NoMatchingOverloadID) { def msg(using Context) = - e"""None of the ${err.overloadedAltsStr(alternatives)} + i"""None of the ${err.overloadedAltsStr(alternatives)} |match ${err.expectedTypeStr(pt)}""" def explain(using Context) = "" } class StableIdentPattern(tree: untpd.Tree, pt: Type)(using Context) extends TypeMsg(StableIdentPatternID) { def msg(using Context) = - e"""Stable identifier required, but $tree found""" + i"""Stable identifier required, but $tree found""" def explain(using Context) = "" } @@ -2290,32 +2289,32 @@ class IllegalSuperAccessor(base: Symbol, memberName: Name, targetName: Name, } hl(i"super[$staticSuperName].$memberName") } - ex"""$base cannot be defined due to a conflict between its parents when - |implementing a super-accessor for $memberName in $accMixin: - | - |1. One of its parent (${accMixin.name}) contains a call $superCall in its body, - | and when a super-call in a trait is written without an explicit parent - | listed in brackets, it is implemented by a generated super-accessor in - | the class that extends this trait based on the linearization order of - | the class. - |2. Because ${otherMixin.name} comes before ${accMixin.name} in the linearization - | order of ${base.name}, and because ${otherMixin.name} overrides $memberName, - | the super-accessor in ${base.name} is implemented as a call to - | $resolvedSuperCall. - |3. However, - | ${otherTp.widenExpr} (the type of $resolvedSuperCall in ${base.name}) - | is not a subtype of - | ${accTp.widenExpr} (the type of $memberName in $accMixin). - | Hence, the super-accessor that needs to be generated in ${base.name} - | is illegal. - | - |Here are two possible ways to resolve this: - | - |1. Change the linearization order of ${base.name} such that - | ${accMixin.name} comes before ${otherMixin.name}. - |2. Alternatively, replace $superCall in the body of $accMixin by a - | super-call to a specific parent, e.g. $staticSuperCall - |""" + i"""$base cannot be defined due to a conflict between its parents when + |implementing a super-accessor for $memberName in $accMixin: + | + |1. One of its parent (${accMixin.name}) contains a call $superCall in its body, + | and when a super-call in a trait is written without an explicit parent + | listed in brackets, it is implemented by a generated super-accessor in + | the class that extends this trait based on the linearization order of + | the class. + |2. Because ${otherMixin.name} comes before ${accMixin.name} in the linearization + | order of ${base.name}, and because ${otherMixin.name} overrides $memberName, + | the super-accessor in ${base.name} is implemented as a call to + | $resolvedSuperCall. + |3. However, + | ${otherTp.widenExpr} (the type of $resolvedSuperCall in ${base.name}) + | is not a subtype of + | ${accTp.widenExpr} (the type of $memberName in $accMixin). + | Hence, the super-accessor that needs to be generated in ${base.name} + | is illegal. + | + |Here are two possible ways to resolve this: + | + |1. Change the linearization order of ${base.name} such that + | ${accMixin.name} comes before ${otherMixin.name}. + |2. Alternatively, replace $superCall in the body of $accMixin by a + | super-call to a specific parent, e.g. $staticSuperCall + |""" } def explain(using Context) = "" } @@ -2325,22 +2324,22 @@ class TraitParameterUsedAsParentPrefix(cls: Symbol)(using Context) def msg(using Context) = s"${cls.show} cannot extend from a parent that is derived via its own parameters" def explain(using Context) = - ex""" - |The parent class/trait that ${cls.show} extends from is obtained from - |the parameter of ${cls.show}. This is disallowed in order to prevent - |outer-related Null Pointer Exceptions in Scala. - | - |In order to fix this issue consider directly extending from the parent rather - |than obtaining it from the parameters of ${cls.show}. - |""" + i""" + |The parent class/trait that ${cls.show} extends from is obtained from + |the parameter of ${cls.show}. This is disallowed in order to prevent + |outer-related Null Pointer Exceptions in Scala. + | + |In order to fix this issue consider directly extending from the parent rather + |than obtaining it from the parameters of ${cls.show}. + |""" } class UnknownNamedEnclosingClassOrObject(name: TypeName)(using Context) extends ReferenceMsg(UnknownNamedEnclosingClassOrObjectID) { def msg(using Context) = - e"""no enclosing class or object is named '${hl(name.show)}'""" + i"""no enclosing class or object is named '${hl(name.show)}'""" def explain(using Context) = - ex""" + i""" |The class or object named '${hl(name.show)}' was used as a visibility |modifier, but could not be resolved. Make sure that |'${hl(name.show)}' is not misspelled and has been imported into the @@ -2367,10 +2366,10 @@ class ErasedTypesCanOnlyBeFunctionTypes()(using Context) class CaseClassMissingNonImplicitParamList(cdef: untpd.TypeDef)(using Context) extends SyntaxMsg(CaseClassMissingNonImplicitParamListID) { def msg(using Context) = - e"""|A ${hl("case class")} must have at least one leading non-implicit parameter list""" + i"""|A ${hl("case class")} must have at least one leading non-implicit parameter list""" def explain(using Context) = - e"""|${cdef.name} must have at least one leading non-implicit parameter list, + i"""|${cdef.name} must have at least one leading non-implicit parameter list, | if you're aiming to have a case class parametrized only by implicit ones, you should | add an explicit ${hl("()")} as the first parameter list to ${cdef.name}.""" } @@ -2380,7 +2379,7 @@ class EnumerationsShouldNotBeEmpty(cdef: untpd.TypeDef)(using Context) def msg(using Context) = "Enumerations must contain at least one case" def explain(using Context) = - e"""|Enumeration ${cdef.name} must contain at least one case + i"""|Enumeration ${cdef.name} must contain at least one case |Example Usage: | ${hl("enum")} ${cdef.name} { | ${hl("case")} Option1, Option2 @@ -2393,7 +2392,7 @@ class TypedCaseDoesNotExplicitlyExtendTypedEnum(enumDef: Symbol, caseDef: untpd. def msg(using Context) = i"explicit extends clause needed because both enum case and enum class have type parameters" def explain(using Context) = - e"""Enumerations where the enum class as well as the enum case have type parameters need + i"""Enumerations where the enum class as well as the enum case have type parameters need |an explicit extends. |for example: | ${hl("enum")} ${enumDef.name}[T] { @@ -2404,18 +2403,18 @@ class TypedCaseDoesNotExplicitlyExtendTypedEnum(enumDef: Symbol, caseDef: untpd. class IllegalRedefinitionOfStandardKind(kindType: String, name: Name)(using Context) extends SyntaxMsg(IllegalRedefinitionOfStandardKindID) { - def msg(using Context) = e"illegal redefinition of standard $kindType $name" + def msg(using Context) = i"illegal redefinition of standard $kindType $name" def explain(using Context) = - e"""| "$name" is a standard Scala core `$kindType` + i"""| "$name" is a standard Scala core `$kindType` | Please choose a different name to avoid conflicts |""" } class NoExtensionMethodAllowed(mdef: untpd.DefDef)(using Context) extends SyntaxMsg(NoExtensionMethodAllowedID) { - def msg(using Context) = e"No extension method allowed here, since collective parameters are given" + def msg(using Context) = i"No extension method allowed here, since collective parameters are given" def explain(using Context) = - e"""|Extension method: + i"""|Extension method: | `${mdef}` |is defined inside an extension clause which has collective parameters. |""" @@ -2426,7 +2425,7 @@ class ExtensionMethodCannotHaveTypeParams(mdef: untpd.DefDef)(using Context) def msg(using Context) = i"Extension method cannot have type parameters since some were already given previously" def explain(using Context) = - e"""|Extension method: + i"""|Extension method: | `${mdef}` |has type parameters `[${mdef.leadingTypeParams.map(_.show).mkString(",")}]`, while the extension clause has |it's own type parameters. Please consider moving these to the extension clause's type parameter list. @@ -2435,18 +2434,18 @@ class ExtensionMethodCannotHaveTypeParams(mdef: untpd.DefDef)(using Context) class ExtensionCanOnlyHaveDefs(mdef: untpd.Tree)(using Context) extends SyntaxMsg(ExtensionCanOnlyHaveDefsID) { - def msg(using Context) = e"Only methods allowed here, since collective parameters are given" + def msg(using Context) = i"Only methods allowed here, since collective parameters are given" def explain(using Context) = - e"""Extension clauses can only have `def`s + i"""Extension clauses can only have `def`s | `${mdef.show}` is not a valid expression here. |""" } class UnexpectedPatternForSummonFrom(tree: Tree[_])(using Context) extends SyntaxMsg(UnexpectedPatternForSummonFromID) { - def msg(using Context) = e"Unexpected pattern for summonFrom. Expected ${hl("`x: T`")} or ${hl("`_`")}" + def msg(using Context) = i"Unexpected pattern for summonFrom. Expected ${hl("`x: T`")} or ${hl("`_`")}" def explain(using Context) = - e"""|The pattern "${tree.show}" provided in the ${hl("case")} expression of the ${hl("summonFrom")}, + i"""|The pattern "${tree.show}" provided in the ${hl("case")} expression of the ${hl("summonFrom")}, | needs to be of the form ${hl("`x: T`")} or ${hl("`_`")}. | | Example usage: @@ -2465,26 +2464,26 @@ class AnonymousInstanceCannotBeEmpty(impl: untpd.Template)(using Context) extends SyntaxMsg(AnonymousInstanceCannotBeEmptyID) { def msg(using Context) = i"anonymous instance must implement a type or have at least one extension method" def explain(using Context) = - e"""|Anonymous instances cannot be defined with an empty body. The block + i"""|Anonymous instances cannot be defined with an empty body. The block |`${impl.show}` should either contain an implemented type or at least one extension method. |""" } class ModifierNotAllowedForDefinition(flag: Flag)(using Context) extends SyntaxMsg(ModifierNotAllowedForDefinitionID) { - def msg(using Context) = e"Modifier ${hl(flag.flagsString)} is not allowed for this definition" + def msg(using Context) = i"Modifier ${hl(flag.flagsString)} is not allowed for this definition" def explain(using Context) = "" } class RedundantModifier(flag: Flag)(using Context) extends SyntaxMsg(RedundantModifierID) { - def msg(using Context) = e"Modifier ${hl(flag.flagsString)} is redundant for this definition" + def msg(using Context) = i"Modifier ${hl(flag.flagsString)} is redundant for this definition" def explain(using Context) = "" } class InvalidReferenceInImplicitNotFoundAnnotation(typeVar: String, owner: String)(using Context) extends ReferenceMsg(InvalidReferenceInImplicitNotFoundAnnotationID) { - def msg(using Context) = e"""|Invalid reference to a type variable ${hl(typeVar)} found in the annotation argument. + def msg(using Context) = i"""|Invalid reference to a type variable ${hl(typeVar)} found in the annotation argument. |The variable does not occur as a parameter in the scope of ${hl(owner)}. |""" def explain(using Context) = "" @@ -2496,7 +2495,7 @@ class CaseClassInInlinedCode(tree: tpd.Tree)(using Context) def defKind = if tree.symbol.is(Module) then "object" else "class" def msg(using Context) = s"Case $defKind definitions are not allowed in inline methods or quoted code. Use a normal $defKind instead." def explain(using Context) = - e"""Case class/object definitions generate a considerable footprint in code size. + i"""Case class/object definitions generate a considerable footprint in code size. |Inlining such definition would multiply this footprint for each call site. |""" } @@ -2507,7 +2506,7 @@ class ImplicitSearchTooLargeWarning(limit: Int, openSearchPairs: List[(Candidate def showQuery(query: (Candidate, Type))(using Context): String = i" ${query._1.ref.symbol.showLocated} for ${query._2}}" def msg(using Context) = - e"""Implicit search problem too large. + i"""Implicit search problem too large. |an implicit search was terminated with failure after trying $limit expressions. |The root candidate for the search was: | @@ -2518,7 +2517,7 @@ class ImplicitSearchTooLargeWarning(limit: Int, openSearchPairs: List[(Candidate |Larger values might make a very large search problem succeed. |""" def explain(using Context) = - e"""The overflow happened with the following lists of tried expressions and target types, + i"""The overflow happened with the following lists of tried expressions and target types, |starting with the root query: | |${openSearchPairs.reverse.map(showQuery)}%\n% @@ -2526,10 +2525,10 @@ class ImplicitSearchTooLargeWarning(limit: Int, openSearchPairs: List[(Candidate class TargetNameOnTopLevelClass(symbol: Symbol)(using Context) extends SyntaxMsg(TargetNameOnTopLevelClassID): - def msg(using Context) = e"${hl("@targetName")} annotation not allowed on top-level $symbol" + def msg(using Context) = i"${hl("@targetName")} annotation not allowed on top-level $symbol" def explain(using Context) = val annot = symbol.getAnnotation(defn.TargetNameAnnot).get - e"""The @targetName annotation may be applied to a top-level ${hl("val")} or ${hl("def")}, but not + i"""The @targetName annotation may be applied to a top-level ${hl("val")} or ${hl("def")}, but not |a top-level ${hl("class")}, ${hl("trait")}, or ${hl("object")}. | |This restriction is due to the naming convention of Java classfiles, whose filenames @@ -2547,7 +2546,7 @@ extends SyntaxMsg(TargetNameOnTopLevelClassID): class NotClassType(tp: Type)(using Context) extends TypeMsg(NotClassTypeID), ShowMatchTrace(tp): - def msg(using Context) = ex"$tp is not a class type" + def msg(using Context) = i"$tp is not a class type" def explain(using Context) = "" class MissingImplicitArgument( @@ -2592,7 +2591,7 @@ class MissingImplicitArgument( def userDefinedErrorString(raw: String, paramNames: List[String], args: List[Type]): String = { def translate(name: String): Option[String] = { val idx = paramNames.indexOf(name) - if (idx >= 0) Some(ex"${args(idx)}") else None + if (idx >= 0) Some(i"${args(idx)}") else None } """\$\{\s*([^}\s]+)\s*\}""".r.replaceAllIn(raw, (_: Regex.Match) match { @@ -2614,7 +2613,7 @@ class MissingImplicitArgument( s"Ambiguous given instances: ${ambi.explanation}${location("of")}" def defaultImplicitNotFoundMessage = - ex"No given instance of type $pt was found${location("for")}" + i"No given instance of type $pt was found${location("for")}" /** Construct a custom error message given an ambiguous implicit * candidate `alt` and a user defined message `raw`. @@ -2725,7 +2724,7 @@ class MissingImplicitArgument( case _ => defaultAmbiguousImplicitMsg(ambi) case ambi @ TooUnspecific(target) => - ex"""No implicit search was attempted${location("for")} + i"""No implicit search was attempted${location("for")} |since the expected type $target is not specific enough""" case _ => val shortMessage = userDefinedImplicitNotFoundParamMessage @@ -2759,15 +2758,15 @@ extends ReferenceMsg(CannotBeAccessedID): val alts = tpe.denot.alternatives.map(_.symbol).filter(_.exists) val whatCanNot = alts match case Nil => - e"$name cannot" + i"$name cannot" case sym :: Nil => - e"${if (sym.owner == pre.typeSymbol) sym.show else sym.showLocated} cannot" + i"${if (sym.owner == pre.typeSymbol) sym.show else sym.showLocated} cannot" case _ => - e"none of the overloaded alternatives named $name can" + i"none of the overloaded alternatives named $name can" val where = if (ctx.owner.exists) s" from ${ctx.owner.enclosingClass}" else "" val whyNot = new StringBuffer alts.foreach(_.isAccessibleFrom(pre, superAccess, whyNot)) - e"$whatCanNot be accessed as a member of $pre$where.$whyNot" + i"$whatCanNot be accessed as a member of $pre$where.$whyNot" def explain(using Context) = "" diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index b17cc3e9bb94..7ba1f881d6d0 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -781,7 +781,7 @@ object Erasure { val tp = originalQual if tp =:= qual1.tpe.widen then return errorTree(qual1, - ex"Unable to emit reference to ${sym.showLocated}, ${sym.owner} is not accessible in ${ctx.owner.enclosingClass}") + em"Unable to emit reference to ${sym.showLocated}, ${sym.owner} is not accessible in ${ctx.owner.enclosingClass}") tp recur(cast(qual1, castTarget)) } diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 7551306d8b70..f11c8f5313dc 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -169,7 +169,7 @@ class TreeChecker extends Phase with SymTransformer { everDefinedSyms.get(sym) match { case Some(t) => if (t ne tree) - report.warning(em"symbol ${sym.fullName} is defined at least twice in different parts of AST") + report.warning(i"symbol ${sym.fullName} is defined at least twice in different parts of AST") // should become an error case None => everDefinedSyms(sym) = tree @@ -179,7 +179,7 @@ class TreeChecker extends Phase with SymTransformer { if (ctx.settings.YcheckMods.value) tree match { case t: untpd.MemberDef => - if (t.name ne sym.name) report.warning(em"symbol ${sym.fullName} name doesn't correspond to AST: ${t}") + if (t.name ne sym.name) report.warning(s"symbol ${sym.fullName} name doesn't correspond to AST: ${t}") // todo: compare trees inside annotations case _ => } @@ -417,11 +417,11 @@ class TreeChecker extends Phase with SymTransformer { sym == mbr || sym.overriddenSymbol(mbr.owner.asClass) == mbr || mbr.overriddenSymbol(sym.owner.asClass) == sym), - ex"""symbols differ for $tree - |was : $sym - |alternatives by type: $memberSyms%, % of types ${memberSyms.map(_.info)}%, % - |qualifier type : ${qualTpe} - |tree type : ${tree.typeOpt} of class ${tree.typeOpt.getClass}""") + i"""symbols differ for $tree + |was : $sym + |alternatives by type: $memberSyms%, % of types ${memberSyms.map(_.info)}%, % + |qualifier type : ${qualTpe} + |tree type : ${tree.typeOpt} of class ${tree.typeOpt.getClass}""") } checkNotRepeated(super.typedSelect(tree, pt)) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index cf7b08135c7b..017f36934358 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -814,7 +814,7 @@ trait Checking { def checkRealizableBounds(cls: Symbol, pos: SrcPos)(using Context): Unit = { val rstatus = boundsRealizability(cls.thisType) if (rstatus ne Realizable) - report.error(ex"$cls cannot be instantiated since it${rstatus.msg}", pos) + report.error(em"$cls cannot be instantiated since it${rstatus.msg}", pos) } /** Check that pattern `pat` is irrefutable for scrutinee type `sel.tpe`. @@ -1050,7 +1050,7 @@ trait Checking { def checkFeasibleParent(tp: Type, pos: SrcPos, where: => String = "")(using Context): Type = { def checkGoodBounds(tp: Type) = tp match { case tp @ TypeBounds(lo, hi) if !(lo <:< hi) => - report.error(ex"no type exists between low bound $lo and high bound $hi$where", pos) + report.error(em"no type exists between low bound $lo and high bound $hi$where", pos) TypeBounds(hi, hi) case _ => tp @@ -1175,7 +1175,7 @@ trait Checking { parent match { case parent: ClassSymbol => if (parent.is(Case)) - report.error(ex"""case $caseCls has case ancestor $parent, but case-to-case inheritance is prohibited. + report.error(em"""case $caseCls has case ancestor $parent, but case-to-case inheritance is prohibited. |To overcome this limitation, use extractors to pattern match on non-leaf nodes.""", pos) else checkCaseInheritance(parent.superClass, caseCls, pos) case _ => diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index ad6e75a9b8c8..b265c6343651 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -167,7 +167,7 @@ trait TypeAssigner { case _ => false def addendum = err.selectErrorAddendum(tree, qual, qualType, importSuggestionAddendum, foundWithoutNull) val msg: Message = - if tree.name == nme.CONSTRUCTOR then ex"$qualType does not have a constructor".toMessage + if tree.name == nme.CONSTRUCTOR then em"$qualType does not have a constructor" else NotAMember(qualType, tree.name, kind, addendum) errorType(msg, tree.srcPos) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 5e3295f44cb6..0fdb801a99d0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -527,7 +527,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val found = findRef(name, pt, EmptyFlags, EmptyFlags, tree.srcPos) if foundUnderScala2.exists && !(foundUnderScala2 =:= found) then report.migrationWarning( - ex"""Name resolution will change. + em"""Name resolution will change. | currently selected : $foundUnderScala2 | in the future, without -source 3.0-migration: $found""", tree.srcPos) foundUnderScala2 @@ -586,7 +586,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else if ctx.owner.isConstructor && !ctx.owner.isPrimaryConstructor && ctx.owner.owner.unforcedDecls.lookup(tree.name).exists then // we are in the arguments of a this(...) constructor call - errorTree(tree, ex"$tree is not accessible from constructor arguments") + errorTree(tree, em"$tree is not accessible from constructor arguments") else errorTree(tree, MissingIdent(tree, kind, name)) end typedIdent @@ -1528,7 +1528,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // Replace the underspecified expected type by one based on the closure method type defn.PartialFunctionOf(mt.firstParamTypes.head, mt.resultType) else - report.error(ex"result type of lambda is an underspecified SAM type $pt", tree.srcPos) + report.error(em"result type of lambda is an underspecified SAM type $pt", tree.srcPos) pt TypeTree(targetTpe) case _ => @@ -3736,7 +3736,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if (!defn.isFunctionType(pt)) pt match { case SAMType(_) if !pt.classSymbol.hasAnnotation(defn.FunctionalInterfaceAnnot) => - report.warning(ex"${tree.symbol} is eta-expanded even though $pt does not have the @FunctionalInterface annotation.", tree.srcPos) + report.warning(em"${tree.symbol} is eta-expanded even though $pt does not have the @FunctionalInterface annotation.", tree.srcPos) case _ => } simplify(typed(etaExpand(tree, wtp, arity), pt), pt, locked) From 0df7eaef5844e9f9b6b61d5656184b0fd39af13c Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 23 Nov 2022 23:07:54 +0100 Subject: [PATCH 10/14] Drop redundant calls to toMessage Also: use exclusively Message insteax of String for error handling in Scanner and Parser. --- .../src/dotty/tools/dotc/core/Types.scala | 4 +- .../dotty/tools/dotc/inlines/Inlines.scala | 6 +- .../tools/dotc/parsing/JavaParsers.scala | 14 +- .../tools/dotc/parsing/JavaScanners.scala | 25 ++-- .../dotty/tools/dotc/parsing/Parsers.scala | 130 ++++++++---------- .../dotty/tools/dotc/parsing/Scanners.scala | 71 +++++----- .../dotc/parsing/xml/MarkupParsers.scala | 10 +- .../dotty/tools/dotc/reporting/Message.scala | 3 +- .../dotty/tools/dotc/reporting/Reporter.scala | 6 +- .../src/dotty/tools/dotc/typer/Checking.scala | 12 +- .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- 11 files changed, 138 insertions(+), 145 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index f7d68623e609..3b841d3aa051 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -5312,10 +5312,10 @@ object Types { def msg(using Context): Message = ctx.base.errorTypeMsg.get(this) match case Some(m) => m - case None => "error message from previous run no longer available".toMessage + case None => em"error message from previous run no longer available" object UnspecifiedErrorType extends ErrorType { - override def msg(using Context): Message = "unspecified error".toMessage + override def msg(using Context): Message = em"unspecified error" } /* Type used to track Select nodes that could not resolve a member and their qualifier is a scala.Dynamic. */ diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index b5923154c088..9408ece0d4ac 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -156,9 +156,9 @@ object Inlines: else ("successive inlines", ctx.settings.XmaxInlines) errorTree( tree, - i"""|Maximal number of $reason (${setting.value}) exceeded, - |Maybe this is caused by a recursive inline method? - |You can use ${setting.name} to change the limit.""".toMessage, + em"""|Maximal number of $reason (${setting.value}) exceeded, + |Maybe this is caused by a recursive inline method? + |You can use ${setting.name} to change the limit.""", (tree :: enclosingInlineds).last.srcPos ) if ctx.base.stopInlining && enclosingInlineds.isEmpty then diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index 183845fcf3ec..daeebcbcc17c 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -71,10 +71,10 @@ object JavaParsers { } } - def syntaxError(msg: String, skipIt: Boolean): Unit = + def syntaxError(msg: Message, skipIt: Boolean): Unit = syntaxError(in.offset, msg, skipIt) - def syntaxError(offset: Int, msg: String, skipIt: Boolean): Unit = { + def syntaxError(offset: Int, msg: Message, skipIt: Boolean): Unit = { if (offset > lastErrorOffset) { syntaxError(msg, offset) // no more errors on this token. @@ -178,9 +178,7 @@ object JavaParsers { if (in.token != token) { val offsetToReport = in.offset val msg = - tokenString(token) + " expected but " + - tokenString(in.token) + " found." - + em"${tokenString(token)} expected but ${tokenString(in.token)} found." syntaxError(offsetToReport, msg, skipIt = true) } if (in.token == token) in.nextToken() @@ -271,7 +269,7 @@ object JavaParsers { case FLOAT => in.nextToken(); TypeTree(FloatType) case DOUBLE => in.nextToken(); TypeTree(DoubleType) case BOOLEAN => in.nextToken(); TypeTree(BooleanType) - case _ => syntaxError("illegal start of type", skipIt = true); errorTypeTree + case _ => syntaxError(em"illegal start of type", skipIt = true); errorTypeTree } } @@ -762,7 +760,7 @@ object JavaParsers { accept(SEMI) val names = buf.toList if (names.length < 2) { - syntaxError(start, "illegal import", skipIt = false) + syntaxError(start, em"illegal import", skipIt = false) List() } else { @@ -954,7 +952,7 @@ object JavaParsers { case INTERFACE => interfaceDecl(start, mods) case AT => annotationDecl(start, mods) case CLASS => classDecl(start, mods) - case _ => in.nextToken(); syntaxError("illegal start of type declaration", skipIt = true); List(errorTypeTree) + case _ => in.nextToken(); syntaxError(em"illegal start of type declaration", skipIt = true); List(errorTypeTree) } def tryConstant: Option[Constant] = { diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaScanners.scala b/compiler/src/dotty/tools/dotc/parsing/JavaScanners.scala index 1be8bdae6bd1..d21d4b85b5df 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaScanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaScanners.scala @@ -10,6 +10,7 @@ import JavaTokens._ import scala.annotation.{switch, tailrec} import util.Chars._ import PartialFunction.cond +import core.Decorators.em object JavaScanners { @@ -108,7 +109,7 @@ object JavaScanners { setStrVal() nextChar() else - error("unclosed string literal") + error(em"unclosed string literal") else nextChar() if ch != '\"' then // "" empty string literal @@ -127,7 +128,7 @@ object JavaScanners { setStrVal() } else - error("unclosed character literal") + error(em"unclosed character literal") case '=' => token = EQUALS @@ -298,7 +299,7 @@ object JavaScanners { nextChar() token = DOTDOTDOT } - else error("`.` character expected") + else error(em"`.` character expected") } case ';' => @@ -336,7 +337,7 @@ object JavaScanners { case SU => if (isAtEnd) token = EOF else { - error("illegal character") + error(em"illegal character") nextChar() } @@ -347,7 +348,7 @@ object JavaScanners { getIdentRest() } else { - error("illegal character: " + ch.toInt) + error(em"illegal character: ${ch.toInt}") nextChar() } } @@ -360,7 +361,7 @@ object JavaScanners { case _ => nextChar(); skipLineComment() } @tailrec def skipJavaComment(): Unit = ch match { - case SU => incompleteInputError("unclosed comment") + case SU => incompleteInputError(em"unclosed comment") case '*' => nextChar(); if (ch == '/') nextChar() else skipJavaComment() case _ => nextChar(); skipJavaComment() } @@ -480,7 +481,7 @@ object JavaScanners { nextChar() } if (ch != LF && ch != CR) { // CR-LF is already normalized into LF by `JavaCharArrayReader` - error("illegal text block open delimiter sequence, missing line terminator") + error(em"illegal text block open delimiter sequence, missing line terminator") return } nextChar() @@ -529,7 +530,7 @@ object JavaScanners { // Bail out if the block never did have an end if (!blockClosed) { - error("unclosed text block") + error(em"unclosed text block") return } @@ -642,14 +643,14 @@ object JavaScanners { while (i < len) { val d = digit2int(strVal.charAt(i), base) if (d < 0) { - error("malformed integer number") + error(em"malformed integer number") return 0 } if (value < 0 || limit / (base / divider) < value || limit - (d / divider) < value * (base / divider) && !(negated && limit == value * base - 1 + d)) { - error("integer number too large") + error(em"integer number too large") return 0 } value = value * base + d @@ -666,11 +667,11 @@ object JavaScanners { try { val value: Double = java.lang.Double.valueOf(strVal.toString).nn.doubleValue() if (value > limit) - error("floating point number too large") + error(em"floating point number too large") if (negated) -value else value } catch { case _: NumberFormatException => - error("malformed floating point number") + error(em"malformed floating point number") 0.0 } } diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 7a36523e87bf..4e061bcb0b19 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -143,21 +143,12 @@ object Parsers { syntaxError(msg, Span(offset, offset + length)) lastErrorOffset = in.offset - def syntaxError(msg: => String, offset: Int): Unit = - syntaxError(msg.toMessage, offset) - - def syntaxError(msg: => String): Unit = - syntaxError(msg, in.offset) - /** Unconditionally issue an error at given span, without * updating lastErrorOffset. */ def syntaxError(msg: Message, span: Span): Unit = report.error(msg, source.atSpan(span)) - def syntaxError(msg: => String, span: Span): Unit = - syntaxError(msg.toMessage, span) - def unimplementedExpr(using Context): Select = Select(scalaDot(nme.Predef), nme.???) } @@ -288,9 +279,6 @@ object Parsers { syntaxError(msg, offset) skip() - def syntaxErrorOrIncomplete(msg: => String): Unit = - syntaxErrorOrIncomplete(msg.toMessage, in.offset) - def syntaxErrorOrIncomplete(msg: Message, span: Span): Unit = if in.token == EOF then incompleteInputError(msg) @@ -346,7 +334,7 @@ object Parsers { in.nextToken() recur(true, endSeen) else if in.token == END then - if endSeen then syntaxError("duplicate end marker") + if endSeen then syntaxError(em"duplicate end marker") checkEndMarker(stats) recur(sepSeen, endSeen = true) else if isStatSeqEnd || in.token == altEnd then @@ -358,7 +346,7 @@ object Parsers { val statFollows = mustStartStatTokens.contains(found) syntaxError( if noPrevStat then IllegalStartOfStatement(what, isModifier, statFollows) - else i"end of $what expected but ${showToken(found)} found".toMessage) + else em"end of $what expected but ${showToken(found)} found") if mustStartStatTokens.contains(found) then false // it's a statement that might be legal in an outer context else @@ -460,7 +448,7 @@ object Parsers { */ def convertToParam(tree: Tree, mods: Modifiers): ValDef = def fail() = - syntaxError(s"not a legal formal parameter for a function literal", tree.span) + syntaxError(em"not a legal formal parameter for a function literal", tree.span) makeParameter(nme.ERROR, tree, mods) tree match case param: ValDef => @@ -618,11 +606,11 @@ object Parsers { if in.isNewLine && !(nextIndentWidth < startIndentWidth) then warning( if startIndentWidth <= nextIndentWidth then - i"""Line is indented too far to the right, or a `{` is missing before: - | - |${t.tryToShow}""".toMessage + em"""Line is indented too far to the right, or a `{` is missing before: + | + |${t.tryToShow}""" else - in.spaceTabMismatchMsg(startIndentWidth, nextIndentWidth).toMessage, + in.spaceTabMismatchMsg(startIndentWidth, nextIndentWidth), in.next.offset ) t @@ -635,7 +623,7 @@ object Parsers { if in.isNewLine then val nextIndentWidth = in.indentWidth(in.next.offset) if in.currentRegion.indentWidth < nextIndentWidth then - warning(i"Line is indented too far to the right, or a `{` or `:` is missing".toMessage, in.next.offset) + warning(em"Line is indented too far to the right, or a `{` or `:` is missing", in.next.offset) /* -------- REWRITES ----------------------------------------------------------- */ @@ -1234,7 +1222,7 @@ object Parsers { null } catch { - case ex: FromDigitsException => syntaxErrorOrIncomplete(ex.getMessage) + case ex: FromDigitsException => syntaxErrorOrIncomplete(ex.getMessage.toMessage) } Literal(Constant(value)) } @@ -1377,7 +1365,7 @@ object Parsers { else in.nextToken() if in.token != INDENT && in.token != LBRACE then - syntaxErrorOrIncomplete(i"indented definitions expected, ${in} found") + syntaxErrorOrIncomplete(em"indented definitions expected, ${in} found") else newLineOptWhenFollowedBy(LBRACE) @@ -1418,7 +1406,7 @@ object Parsers { if in.token == END then val start = in.skipToken() if stats.isEmpty || !matchesAndSetEnd(stats.last) then - syntaxError("misaligned end marker", Span(start, in.lastCharOffset)) + syntaxError(em"misaligned end marker", Span(start, in.lastCharOffset)) else if overlapsPatch(source, Span(start, start)) then patch(source, Span(start, start), "") patch(source, Span(start, in.lastCharOffset), s"} // end $endName") @@ -1505,7 +1493,7 @@ object Parsers { TermLambdaTypeTree(params.asInstanceOf[List[ValDef]], resultType) else if imods.isOneOf(Given | Erased | Impure) then if imods.is(Given) && params.isEmpty then - syntaxError("context function types require at least one parameter", paramSpan) + syntaxError(em"context function types require at least one parameter", paramSpan) FunctionWithMods(params, resultType, imods) else if !ctx.settings.YkindProjector.isDefault then val (newParams :+ newResultType, tparams) = replaceKindProjectorPlaceholders(params :+ resultType): @unchecked @@ -1568,7 +1556,7 @@ object Parsers { if (isFunction(body)) PolyFunction(tparams, body) else { - syntaxError("Implementation restriction: polymorphic function types must have a value parameter", arrowOffset) + syntaxError(em"Implementation restriction: polymorphic function types must have a value parameter", arrowOffset) Ident(nme.ERROR.toTypeName) } } @@ -1722,7 +1710,7 @@ object Parsers { val hint = if inPattern then "Use lower cased variable name without the `$` instead" else "To use a given Type[T] in a quote just write T directly" - syntaxError(s"$msg\n\nHint: $hint", Span(start, in.lastOffset)) + syntaxError(em"$msg\n\nHint: $hint", Span(start, in.lastOffset)) Ident(nme.ERROR.toTypeName) else Splice(expr) @@ -1804,7 +1792,7 @@ object Parsers { if (!ctx.settings.YkindProjector.isDefault) { def fail(): Tree = { syntaxError( - "λ requires a single argument of the form X => ... or (X, Y) => ...", + em"λ requires a single argument of the form X => ... or (X, Y) => ...", Span(startOffset(t), in.lastOffset) ) AppliedTypeTree(applied, args) @@ -1899,10 +1887,10 @@ object Parsers { val tp = paramTypeOf(core) val tp1 = tp match case ImpureByNameTypeTree(tp1) => - syntaxError("explicit captureSet is superfluous for impure call-by-name type", start) + syntaxError(em"explicit captureSet is superfluous for impure call-by-name type", start) tp1 case CapturingTypeTree(_, tp1: ByNameTypeTree) => - syntaxError("only one captureSet is allowed here", start) + syntaxError(em"only one captureSet is allowed here", start) tp1 case _: ByNameTypeTree if startTpOffset > endCsOffset => report.warning( @@ -2094,7 +2082,7 @@ object Parsers { if (isFunction(body)) PolyFunction(tparams, body) else { - syntaxError("Implementation restriction: polymorphic function literals must have a value parameter", arrowOffset) + syntaxError(em"Implementation restriction: polymorphic function literals must have a value parameter", arrowOffset) errorTermTree(arrowOffset) } } @@ -2355,7 +2343,7 @@ object Parsers { atSpan(start, in.offset) { if in.token == CTXARROW then if params.isEmpty then - syntaxError("context function literals require at least one formal parameter", Span(start, in.lastOffset)) + syntaxError(em"context function literals require at least one formal parameter", Span(start, in.lastOffset)) in.nextToken() else accept(ARROW) @@ -2778,10 +2766,10 @@ object Parsers { CaseDef(pat, grd, atSpan(accept(ARROW)) { if exprOnly then if in.indentSyntax && in.isAfterLineEnd && in.token != INDENT then - warning(i"""Misleading indentation: this expression forms part of the preceding catch case. - |If this is intended, it should be indented for clarity. - |Otherwise, if the handler is intended to be empty, use a multi-line catch with - |an indented case.""".toMessage) + warning(em"""Misleading indentation: this expression forms part of the preceding catch case. + |If this is intended, it should be indented for clarity. + |Otherwise, if the handler is intended to be empty, use a multi-line catch with + |an indented case.""") expr() else block() }) @@ -3006,7 +2994,7 @@ object Parsers { if in.token == THIS then if sourceVersion.isAtLeast(future) then deprecationWarning( - "The [this] qualifier will be deprecated in the future; it should be dropped.".toMessage) + em"The [this] qualifier will be deprecated in the future; it should be dropped.") in.nextToken() mods | Local else mods.withPrivateWithin(ident().toTypeName) @@ -3097,7 +3085,7 @@ object Parsers { def variance(vflag: FlagSet): FlagSet = if ownerKind == ParamOwner.Def || ownerKind == ParamOwner.TypeParam then - syntaxError(i"no `+/-` variance annotation allowed here") + syntaxError(em"no `+/-` variance annotation allowed here") in.nextToken() EmptyFlags else @@ -3188,7 +3176,7 @@ object Parsers { addMod(mods, mod) else if (!(mods.flags &~ (ParamAccessor | Inline | impliedMods.flags)).isEmpty) - syntaxError("`val` or `var` expected") + syntaxError(em"`val` or `var` expected") if (firstClause && ofCaseClass) mods else mods | PrivateLocal } @@ -3234,7 +3222,7 @@ object Parsers { else paramMods() if givenOnly && !impliedMods.is(Given) then - syntaxError("`using` expected") + syntaxError(em"`using` expected") val isParams = !impliedMods.is(Given) || startParamTokens.contains(in.token) @@ -3313,19 +3301,19 @@ object Parsers { in.languageImportContext = in.languageImportContext.importContext(imp, NoSymbol) for case ImportSelector(id @ Ident(imported), EmptyTree, _) <- selectors do if Feature.handleGlobalLanguageImport(prefix, imported) && !outermost then - syntaxError(i"this language import is only allowed at the toplevel", id.span) + syntaxError(em"this language import is only allowed at the toplevel", id.span) if allSourceVersionNames.contains(imported) && prefix.isEmpty then if !outermost then - syntaxError(i"source version import is only allowed at the toplevel", id.span) + syntaxError(em"source version import is only allowed at the toplevel", id.span) else if ctx.compilationUnit.sourceVersion.isDefined then - syntaxError(i"duplicate source version import", id.span) + syntaxError(em"duplicate source version import", id.span) else if illegalSourceVersionNames.contains(imported) then val candidate = val nonMigration = imported.toString.replace("-migration", "") validSourceVersionNames.find(_.show == nonMigration) - val baseMsg = i"`$imported` is not a valid source version" + val baseMsg = em"`$imported` is not a valid source version" val msg = candidate match - case Some(member) => i"$baseMsg, did you mean language.`$member`?" + case Some(member) => baseMsg.append(i", did you mean language.`$member`?") case _ => baseMsg syntaxError(msg, id.span) else @@ -3388,7 +3376,7 @@ object Parsers { case _ => if isIdent(nme.raw.STAR) then wildcardSelector() else - if !idOK then syntaxError(i"named imports cannot follow wildcard imports") + if !idOK then syntaxError(em"named imports cannot follow wildcard imports") namedSelector(termIdent()) } @@ -3541,7 +3529,7 @@ object Parsers { val vparamss = paramClauses(numLeadParams = numLeadParams) if (vparamss.isEmpty || vparamss.head.take(1).exists(_.mods.isOneOf(GivenOrImplicit))) in.token match { - case LBRACKET => syntaxError("no type parameters allowed here") + case LBRACKET => syntaxError(em"no type parameters allowed here") case EOF => incompleteInputError(AuxConstructorNeedsNonImplicitParameter()) case _ => syntaxError(AuxConstructorNeedsNonImplicitParameter(), nameStart) } @@ -3634,13 +3622,13 @@ object Parsers { case TypeBoundsTree(EmptyTree, upper, _) => rhs = MatchTypeTree(upper, mtt.selector, mtt.cases) case _ => - syntaxError(i"cannot combine lower bound and match type alias", eqOffset) + syntaxError(em"cannot combine lower bound and match type alias", eqOffset) } case _ => if mods.is(Opaque) then rhs = TypeBoundsTree(bounds.lo, bounds.hi, rhs) else - syntaxError(i"cannot combine bound and alias", eqOffset) + syntaxError(em"cannot combine bound and alias", eqOffset) } makeTypeDef(rhs) } @@ -3721,7 +3709,7 @@ object Parsers { private def checkAccessOnly(mods: Modifiers, where: String): Modifiers = val mods1 = mods & (AccessFlags | Enum) if mods1 ne mods then - syntaxError(s"Only access modifiers are allowed on enum $where") + syntaxError(em"Only access modifiers are allowed on enum $where") mods1 /** EnumDef ::= id ClassConstr InheritClauses EnumBody @@ -3777,17 +3765,17 @@ object Parsers { vparamss: List[List[Tree]], stat: Tree): Unit = stat match { case stat: DefDef => if stat.mods.is(ExtensionMethod) && vparamss.nonEmpty then - syntaxError(i"no extension method allowed here since leading parameter was already given", stat.span) + syntaxError(em"no extension method allowed here since leading parameter was already given", stat.span) else if !stat.mods.is(ExtensionMethod) && vparamss.isEmpty then - syntaxError(i"an extension method is required here", stat.span) + syntaxError(em"an extension method is required here", stat.span) else if tparams.nonEmpty && stat.leadingTypeParams.nonEmpty then - syntaxError(i"extension method cannot have type parameters since some were already given previously", + syntaxError(em"extension method cannot have type parameters since some were already given previously", stat.leadingTypeParams.head.span) else if stat.rhs.isEmpty then - syntaxError(i"extension method cannot be abstract", stat.span) + syntaxError(em"extension method cannot be abstract", stat.span) case EmptyTree => case stat => - syntaxError(i"extension clause can only define methods", stat.span) + syntaxError(em"extension clause can only define methods", stat.span) } /** GivenDef ::= [GivenSig] (AnnotType [‘=’ Expr] | StructuralInstance) @@ -3853,7 +3841,7 @@ object Parsers { do () leadParamss ++= paramClauses(givenOnly = true, numLeadParams = nparams) if in.isColon then - syntaxError("no `:` expected here") + syntaxError(em"no `:` expected here") in.nextToken() val methods: List[Tree] = if in.token == EXPORT then @@ -3864,7 +3852,7 @@ object Parsers { in.observeIndented() newLineOptWhenFollowedBy(LBRACE) if in.isNestedStart then inDefScopeBraces(extMethods(nparams)) - else { syntaxErrorOrIncomplete("Extension without extension methods") ; Nil } + else { syntaxErrorOrIncomplete(em"Extension without extension methods") ; Nil } val result = atSpan(start)(ExtMethods(joinParams(tparams, leadParamss.toList), methods)) val comment = in.getDocComment(start) if comment.isDefined then @@ -3897,7 +3885,7 @@ object Parsers { meths += defDefOrDcl(start, mods, numLeadParams) in.token != EOF && statSepOrEnd(meths, what = "extension method") do () - if meths.isEmpty then syntaxErrorOrIncomplete("`def` expected") + if meths.isEmpty then syntaxErrorOrIncomplete(em"`def` expected") meths.toList } @@ -4085,7 +4073,7 @@ object Parsers { in.token = SELFARROW // suppresses INDENT insertion after `=>` in.nextToken() else - syntaxError("`=>` expected after self type") + syntaxError(em"`=>` expected after self type") makeSelfDef(selfName, selfTpt) } else EmptyValDef @@ -4132,24 +4120,26 @@ object Parsers { def refineStatSeq(): List[Tree] = { val stats = new ListBuffer[Tree] def checkLegal(tree: Tree): List[Tree] = - val problem = tree match + def ok = tree :: Nil + def fail(msg: Message) = + syntaxError(msg, tree.span) + Nil + tree match case tree: ValDef if tree.mods.is(Mutable) => - i"""refinement cannot be a mutable var. - |You can use an explicit getter ${tree.name} and setter ${tree.name}_= instead""" + fail(em"""refinement cannot be a mutable var. + |You can use an explicit getter ${tree.name} and setter ${tree.name}_= instead""") case tree: MemberDef if !(tree.mods.flags & ModifierFlags).isEmpty => - i"refinement cannot be ${(tree.mods.flags & ModifierFlags).flagStrings().mkString("`", "`, `", "`")}" + fail(em"refinement cannot be ${(tree.mods.flags & ModifierFlags).flagStrings().mkString("`", "`, `", "`")}") case tree: DefDef if tree.termParamss.nestedExists(!_.rhs.isEmpty) => - i"refinement cannot have default arguments" + fail(em"refinement cannot have default arguments") case tree: ValOrDefDef => - if tree.rhs.isEmpty then "" - else "refinement cannot have a right-hand side" + if tree.rhs.isEmpty then ok + else fail(em"refinement cannot have a right-hand side") case tree: TypeDef => - if !tree.isClassDef then "" - else "refinement cannot be a class or trait" + if !tree.isClassDef then ok + else fail(em"refinement cannot be a class or trait") case _ => - "this kind of definition cannot be a refinement" - if problem.isEmpty then tree :: Nil - else { syntaxError(problem, tree.span); Nil } + fail(em"this kind of definition cannot be a refinement") while val dclFound = isDclIntro diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 0540ef27a4d3..f1542003f620 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -19,7 +19,7 @@ import rewrites.Rewrites.patch import config.Feature import config.Feature.{migrateTo3, fewerBracesEnabled} import config.SourceVersion.`3.0` -import reporting.{NoProfile, Profile} +import reporting.{NoProfile, Profile, Message} object Scanners { @@ -100,19 +100,22 @@ object Scanners { */ var errOffset: Offset = NoOffset + protected def error(msg: String, off: Offset): Unit = + error(msg.toMessage, off) + /** Generate an error at the given offset */ - def error(msg: String, off: Offset = offset): Unit = { + def error(msg: Message, off: Offset = offset): Unit = { errorButContinue(msg, off) token = ERROR errOffset = off } - def errorButContinue(msg: String, off: Offset = offset): Unit = + def errorButContinue(msg: Message, off: Offset = offset): Unit = report.error(msg, sourcePos(off)) /** signal an error where the input ended in the middle of a token */ - def incompleteInputError(msg: String): Unit = { - report.incompleteInputError(msg.toMessage, sourcePos()) + def incompleteInputError(msg: Message): Unit = { + report.incompleteInputError(msg, sourcePos()) token = EOF errOffset = offset } @@ -159,7 +162,7 @@ object Scanners { // disallow trailing numeric separator char, but continue lexing def checkNoTrailingSeparator(): Unit = if (!litBuf.isEmpty && isNumberSeparator(litBuf.last)) - errorButContinue("trailing separator is not allowed", offset + litBuf.length - 1) + errorButContinue(em"trailing separator is not allowed", offset + litBuf.length - 1) } class Scanner(source: SourceFile, override val startFrom: Offset = 0, profile: Profile = NoProfile, allowIndent: Boolean = true)(using Context) extends ScannerCommon(source) { @@ -192,7 +195,7 @@ object Scanners { val rewriteTargets = List(s.newSyntax, s.oldSyntax, s.indent, s.noindent) val enabled = rewriteTargets.filter(_.value) if (enabled.length > 1) - error(s"illegal combination of -rewrite targets: ${enabled(0).name} and ${enabled(1).name}") + error(em"illegal combination of -rewrite targets: ${enabled(0).name} and ${enabled(1).name}") } private var myLanguageImportContext: Context = ctx @@ -603,9 +606,9 @@ object Scanners { insert(OUTDENT, offset) handleNewIndentWidth(r.enclosing, ir => errorButContinue( - i"""The start of this line does not match any of the previous indentation widths. - |Indentation width of current line : $nextWidth - |This falls between previous widths: ${ir.width} and $lastWidth""")) + em"""The start of this line does not match any of the previous indentation widths. + |Indentation width of current line : $nextWidth + |This falls between previous widths: ${ir.width} and $lastWidth""")) case r => if skipping then if r.enclosing.isClosedByUndentAt(nextWidth) then @@ -628,9 +631,9 @@ object Scanners { profile.recordNewLine() end handleNewLine - def spaceTabMismatchMsg(lastWidth: IndentWidth, nextWidth: IndentWidth) = - i"""Incompatible combinations of tabs and spaces in indentation prefixes. - |Previous indent : $lastWidth + def spaceTabMismatchMsg(lastWidth: IndentWidth, nextWidth: IndentWidth): Message = + em"""Incompatible combinations of tabs and spaces in indentation prefixes. + |Previous indent : $lastWidth |Latest indent : $nextWidth""" def observeColonEOL(inTemplate: Boolean): Unit = @@ -783,12 +786,12 @@ object Scanners { putChar(low) res = true else - error(s"illegal character '${toUnicode(high)}${toUnicode(low)}'") + error(em"illegal character '${toUnicode(high)}${toUnicode(low)}'") else if !strict then putChar(high) res = true else - error(s"illegal character '${toUnicode(high)}' missing low surrogate") + error(em"illegal character '${toUnicode(high)}' missing low surrogate") res } private def atSupplementary(ch: Char, f: Int => Boolean): Boolean = @@ -865,7 +868,7 @@ object Scanners { case _ => base = 10 ; putChar('0') } if (base != 10 && !isNumberSeparator(ch) && digit2int(ch, base) < 0) - error("invalid literal number") + error(em"invalid literal number") } fetchLeadingZero() getNumber() @@ -931,13 +934,13 @@ object Scanners { val isEmptyCharLit = (ch == '\'') getLitChar() if ch == '\'' then - if isEmptyCharLit then error("empty character literal (use '\\'' for single quote)") - else if litBuf.length != 1 then error("illegal codepoint in Char constant: " + litBuf.toString.map(toUnicode).mkString("'", "", "'")) + if isEmptyCharLit then error(em"empty character literal (use '\\'' for single quote)") + else if litBuf.length != 1 then error(em"illegal codepoint in Char constant: ${litBuf.toString.map(toUnicode).mkString("'", "", "'")}") else finishCharLit() - else if isEmptyCharLit then error("empty character literal") - else error("unclosed character literal") + else if isEmptyCharLit then error(em"empty character literal") + else error(em"unclosed character literal") case _ => - error("unclosed character literal") + error(em"unclosed character literal") } } fetchSingleQuote() @@ -968,7 +971,7 @@ object Scanners { case SU => if (isAtEnd) token = EOF else { - error("illegal character") + error(em"illegal character") nextChar() } case _ => @@ -994,7 +997,7 @@ object Scanners { else if isSupplementary(ch, isUnicodeIdentifierStart) then getIdentRest() else { - error(s"illegal character '${toUnicode(ch)}'") + error(em"illegal character '${toUnicode(ch)}'") nextChar() } fetchOther() @@ -1024,7 +1027,7 @@ object Scanners { if (ch == '/') nextChar() else skipComment() } - else if (ch == SU) incompleteInputError("unclosed comment") + else if (ch == SU) incompleteInputError(em"unclosed comment") else { nextChar(); skipComment() } def nestedComment() = { nextChar(); skipComment() } val start = lastCharOffset @@ -1104,11 +1107,11 @@ object Scanners { nextChar() finishNamedToken(BACKQUOTED_IDENT, target = this) if (name.length == 0) - error("empty quoted identifier") + error(em"empty quoted identifier") else if (name == nme.WILDCARD) - error("wildcard invalid as backquoted identifier") + error(em"wildcard invalid as backquoted identifier") } - else error("unclosed quoted identifier") + else error(em"unclosed quoted identifier") } private def getIdentRest(): Unit = (ch: @switch) match { @@ -1202,7 +1205,7 @@ object Scanners { nextChar() token = STRINGLIT } - else error("unclosed string literal") + else error(em"unclosed string literal") } private def getRawStringLit(): Unit = @@ -1216,7 +1219,7 @@ object Scanners { getRawStringLit() } else if (ch == SU) - incompleteInputError("unclosed multi-line string literal") + incompleteInputError(em"unclosed multi-line string literal") else { putChar(ch) nextRawChar() @@ -1286,7 +1289,7 @@ object Scanners { else if atSupplementary(ch, isUnicodeIdentifierStart) then getInterpolatedIdentRest(hasSupplement = true) else - error("invalid string interpolation: `$$`, `$\"`, `$`ident or `$`BlockExpr expected", off = charOffset - 2) + error("invalid string interpolation: `$$`, `$\"`, `$`ident or `$`BlockExpr expected".toMessage, off = charOffset - 2) putChar('$') getStringPart(multiLine) } @@ -1294,9 +1297,9 @@ object Scanners { val isUnclosedLiteral = !isUnicodeEscape && (ch == SU || (!multiLine && (ch == CR || ch == LF))) if (isUnclosedLiteral) if (multiLine) - incompleteInputError("unclosed multi-line string literal") + incompleteInputError(em"unclosed multi-line string literal") else - error("unclosed string literal") + error(em"unclosed string literal") else { putChar(ch) nextRawChar() @@ -1448,7 +1451,7 @@ object Scanners { } def checkNoLetter(): Unit = if (isIdentifierPart(ch) && ch >= ' ') - error("Invalid literal number") + error(em"Invalid literal number") /** Read a number into strVal and set base */ @@ -1531,7 +1534,7 @@ object Scanners { def resume(lastTokenData: TokenData): Unit = { this.copyFrom(lastTokenData) if (next.token != EMPTY && !ctx.reporter.hasErrors) - error("unexpected end of input: possible missing '}' in XML block") + error(em"unexpected end of input: possible missing '}' in XML block") nextToken() } diff --git a/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala b/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala index c82d565de7cb..77c5a1bf376b 100644 --- a/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala @@ -14,7 +14,7 @@ import Parsers._ import util.Spans._ import core._ import Constants._ -import Decorators.toMessage +import Decorators.{em, toMessage} import util.SourceFile import Utility._ @@ -331,9 +331,9 @@ object MarkupParsers { case c @ TruncatedXMLControl => ifTruncated(c.getMessage) case c @ (MissingEndTagControl | ConfusedAboutBracesControl) => - parser.syntaxError(c.getMessage + debugLastElem + ">", debugLastPos) + parser.syntaxError(em"${c.getMessage}$debugLastElem>", debugLastPos) case _: ArrayIndexOutOfBoundsException => - parser.syntaxError("missing end tag in XML literal for <%s>" format debugLastElem, debugLastPos) + parser.syntaxError(em"missing end tag in XML literal for <$debugLastElem>", debugLastPos) } finally parser.in.resume(saved) @@ -397,7 +397,7 @@ object MarkupParsers { tree } }, - msg => parser.syntaxError(msg, curOffset) + msg => parser.syntaxError(msg.toMessage, curOffset) ) def escapeToScala[A](op: => A, kind: String): A = { @@ -423,7 +423,7 @@ object MarkupParsers { */ def xScalaPatterns: List[Tree] = escapeToScala(parser.patterns(), "pattern") - def reportSyntaxError(offset: Int, str: String): Unit = parser.syntaxError(str, offset) + def reportSyntaxError(offset: Int, str: String): Unit = parser.syntaxError(str.toMessage, offset) def reportSyntaxError(str: String): Unit = { reportSyntaxError(curOffset, "in XML literal: " + str) nextch() diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index d360db68a8c0..03e4d2dbdfac 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -232,7 +232,8 @@ end Message * disambiguation as an opt-in. The reason is that one usually does not consider all * fine-grained details when writing an error message. If disambiguation is the default, * some tests will show where clauses that look too noisy and that then can be disabled - * when needed. But + * when needed. But if silence is the default, one usually does not realize that + * better info could be obtained by turning disambiguation on. */ abstract class Message(val errorId: ErrorMessageID)(using Context) { self => import Message.* diff --git a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala index 497e77ae4a7c..f5aadac27296 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala @@ -14,7 +14,7 @@ import dotty.tools.dotc.util.NoSourcePosition import java.io.{BufferedReader, PrintWriter} import scala.annotation.internal.sharable import scala.collection.mutable -import core.Decorators.toMessage +import core.Decorators.em object Reporter { /** Convert a SimpleReporter into a real Reporter */ @@ -218,8 +218,8 @@ abstract class Reporter extends interfaces.ReporterResult { def summarizeUnreportedWarnings()(using Context): Unit = for (settingName, count) <- unreportedWarnings do val were = if count == 1 then "was" else "were" - val msg = s"there $were ${countString(count, settingName.tail + " warning")}; re-run with $settingName for details" - report(Warning(msg.toMessage, NoSourcePosition)) + val msg = em"there $were ${countString(count, settingName.tail + " warning")}; re-run with $settingName for details" + report(Warning(msg, NoSourcePosition)) /** Print the summary of warnings and errors */ def printSummary()(using Context): Unit = { diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 017f36934358..5e4f86f6420f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -472,11 +472,11 @@ object Checking { def checkWithDeferred(flag: FlagSet) = if (sym.isOneOf(flag)) fail(AbstractMemberMayNotHaveModifier(sym, flag)) - def checkNoConflict(flag1: FlagSet, flag2: FlagSet, msg: => String) = - if (sym.isAllOf(flag1 | flag2)) fail(msg.toMessage) + def checkNoConflict(flag1: FlagSet, flag2: FlagSet, msg: Message) = + if (sym.isAllOf(flag1 | flag2)) fail(msg) def checkCombination(flag1: FlagSet, flag2: FlagSet) = if sym.isAllOf(flag1 | flag2) then - fail(i"illegal combination of modifiers: `${flag1.flagsString}` and `${flag2.flagsString}` for: $sym".toMessage) + fail(em"illegal combination of modifiers: `${flag1.flagsString}` and `${flag2.flagsString}` for: $sym") def checkApplicable(flag: Flag, ok: Boolean) = if sym.is(flag, butNot = Synthetic) && !ok then fail(ModifierNotAllowedForDefinition(flag)) @@ -521,7 +521,7 @@ object Checking { if !sym.isOneOf(Method | ModuleVal) then fail(TailrecNotApplicable(sym)) else if sym.is(Inline) then - fail("Inline methods cannot be @tailrec".toMessage) + fail(em"Inline methods cannot be @tailrec") if sym.hasAnnotation(defn.TargetNameAnnot) && sym.isClass && sym.isTopLevelClass then fail(TargetNameOnTopLevelClass(sym)) if (sym.hasAnnotation(defn.NativeAnnot)) { @@ -540,7 +540,7 @@ object Checking { fail(CannotExtendAnyVal(sym)) if (sym.isConstructor && !sym.isPrimaryConstructor && sym.owner.is(Trait, butNot = JavaDefined)) val addendum = if ctx.settings.Ydebug.value then s" ${sym.owner.flagsString}" else "" - fail(s"Traits cannot have secondary constructors$addendum".toMessage) + fail(em"Traits cannot have secondary constructors$addendum") checkApplicable(Inline, sym.isTerm && !sym.isOneOf(Mutable | Module)) checkApplicable(Lazy, !sym.isOneOf(Method | Mutable)) if (sym.isType && !sym.isOneOf(Deferred | JavaDefined)) @@ -561,7 +561,7 @@ object Checking { // The issue with `erased inline` is that the erased semantics get lost // as the code is inlined and the reference is removed before the erased usage check. checkCombination(Erased, Inline) - checkNoConflict(Lazy, ParamAccessor, s"parameter may not be `lazy`") + checkNoConflict(Lazy, ParamAccessor, em"parameter may not be `lazy`") } /** Check for illegal or redundant modifiers on modules. This is done separately diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 0fdb801a99d0..3f1e4b175338 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2670,7 +2670,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // Package will not exist if a duplicate type has already been entered, see `tests/neg/1708.scala` errorTree(tree, if pkg.exists then PackageNameAlreadyDefined(pkg) - else i"package ${tree.pid.name} does not exist".toMessage) + else em"package ${tree.pid.name} does not exist") end typedPackageDef def typedAnnotated(tree: untpd.Annotated, pt: Type)(using Context): Tree = { From 9a9ce54e407d7b273f03e746e1211c3a3ba8be4e Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 23 Nov 2022 23:16:42 +0100 Subject: [PATCH 11/14] Drop String overload in ErrorType --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 2 +- compiler/src/dotty/tools/dotc/core/Decorators.scala | 2 +- compiler/src/dotty/tools/dotc/core/NamerOps.scala | 2 +- compiler/src/dotty/tools/dotc/core/Types.scala | 2 -- compiler/src/dotty/tools/dotc/transform/Splicer.scala | 8 ++++---- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index e8aef6e90fee..68f4b75e87de 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -117,7 +117,7 @@ object desugar { if (local.exists) (defctx.owner.thisType select local).dealiasKeepAnnots else { def msg = - s"no matching symbol for ${tp.symbol.showLocated} in ${defctx.owner} / ${defctx.effectiveScope.toList}" + em"no matching symbol for ${tp.symbol.showLocated} in ${defctx.owner} / ${defctx.effectiveScope.toList}" ErrorType(msg).assertingErrorsReported(msg) } case _ => diff --git a/compiler/src/dotty/tools/dotc/core/Decorators.scala b/compiler/src/dotty/tools/dotc/core/Decorators.scala index 0a48ad7a899c..abbdd97daf36 100644 --- a/compiler/src/dotty/tools/dotc/core/Decorators.scala +++ b/compiler/src/dotty/tools/dotc/core/Decorators.scala @@ -287,7 +287,7 @@ object Decorators { assert(ctx.reporter.errorsReported) x } - def assertingErrorsReported(msg: => String)(using Context): T = { + def assertingErrorsReported(msg: Message)(using Context): T = { assert(ctx.reporter.errorsReported, msg) x } diff --git a/compiler/src/dotty/tools/dotc/core/NamerOps.scala b/compiler/src/dotty/tools/dotc/core/NamerOps.scala index fa0a89349b5e..bba7f97db6ba 100644 --- a/compiler/src/dotty/tools/dotc/core/NamerOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NamerOps.scala @@ -70,7 +70,7 @@ object NamerOps: def findModuleBuddy(name: Name, scope: Scope)(using Context) = { val it = scope.lookupAll(name).filter(_.is(Module)) if (it.hasNext) it.next() - else NoSymbol.assertingErrorsReported(s"no companion $name in $scope") + else NoSymbol.assertingErrorsReported(em"no companion $name in $scope") } /** If a class has one of these flags, it does not get a constructor companion */ diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 3b841d3aa051..0b1258d3801c 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -5304,8 +5304,6 @@ object Types { val et = new PreviousErrorType ctx.base.errorTypeMsg(et) = m et - def apply(s: => String)(using Context): ErrorType = - apply(s.toMessage) end ErrorType class PreviousErrorType extends ErrorType: diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index ea83f276a59c..761a19d122a3 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -77,10 +77,10 @@ object Splicer { ref(defn.Predef_undefined).withType(ErrorType(ex.msg)) case NonFatal(ex) => val msg = - s"""Failed to evaluate macro. - | Caused by ${ex.getClass}: ${if (ex.getMessage == null) "" else ex.getMessage} - | ${ex.getStackTrace.takeWhile(_.getClassName != "dotty.tools.dotc.transform.Splicer$").drop(1).mkString("\n ")} - """.stripMargin + em"""Failed to evaluate macro. + | Caused by ${ex.getClass}: ${if (ex.getMessage == null) "" else ex.getMessage} + | ${ex.getStackTrace.takeWhile(_.getClassName != "dotty.tools.dotc.transform.Splicer$").drop(1).mkString("\n ")} + """ report.error(msg, spliceExpansionPos) ref(defn.Predef_undefined).withType(ErrorType(msg)) } From 27f98560586ed2546fac3150a4df29f3ec159fc1 Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 24 Nov 2022 11:40:30 +0100 Subject: [PATCH 12/14] Convert reporting arguments to messages Using Message instead of String for arguments of report methods. String versions are still retained for external interop. But they should be used only if the argument is a simple string literal. --- .../tools/backend/jvm/BCodeBodyBuilder.scala | 3 +- .../tools/backend/jvm/BCodeHelpers.scala | 11 ++--- .../tools/backend/jvm/BCodeSkelBuilder.scala | 9 ++-- .../backend/jvm/DottyBackendInterface.scala | 3 +- .../dotty/tools/backend/jvm/GenBCode.scala | 7 +-- .../tools/backend/jvm/scalaPrimitives.scala | 5 ++- .../dotty/tools/backend/sjs/JSCodeGen.scala | 2 +- .../tools/backend/sjs/JSExportsGen.scala | 4 +- .../dotty/tools/backend/sjs/JSPositions.scala | 3 +- .../tools/backend/sjs/JSPrimitives.scala | 3 +- .../dotty/tools/dotc/CompilationUnit.scala | 4 +- compiler/src/dotty/tools/dotc/Driver.scala | 6 +-- .../src/dotty/tools/dotc/ast/Desugar.scala | 4 +- .../dotty/tools/dotc/ast/DesugarEnums.scala | 2 +- .../dotty/tools/dotc/ast/MainProxies.scala | 22 +++++----- compiler/src/dotty/tools/dotc/ast/Trees.scala | 2 +- .../dotty/tools/dotc/cc/CheckCaptures.scala | 10 ++--- .../src/dotty/tools/dotc/config/Feature.scala | 4 +- .../dotty/tools/dotc/core/SymbolLoaders.scala | 5 ++- .../tools/dotc/core/tasty/TreePickler.scala | 2 +- .../dotty/tools/dotc/fromtasty/TASTYRun.scala | 3 +- .../dotty/tools/dotc/inlines/Inliner.scala | 6 +-- .../dotty/tools/dotc/parsing/Parsers.scala | 20 ++++----- .../dotty/tools/dotc/parsing/Scanners.scala | 7 +-- .../dotty/tools/dotc/plugins/Plugins.scala | 5 ++- .../dotty/tools/dotc/quoted/Interpreter.scala | 2 +- compiler/src/dotty/tools/dotc/report.scala | 44 ++++++------------- .../dotty/tools/dotc/reporting/Message.scala | 17 +++++++ .../tools/dotc/sbt/ExtractDependencies.scala | 2 +- .../dotc/transform/CompleteJavaEnums.scala | 2 +- .../tools/dotc/transform/ElimRepeated.scala | 4 +- .../dotty/tools/dotc/transform/Erasure.scala | 21 ++++----- .../tools/dotc/transform/ExpandSAMs.scala | 2 +- .../tools/dotc/transform/InlineVals.scala | 4 +- .../dotc/transform/NonLocalReturns.scala | 3 +- .../dotc/transform/PCPCheckAndHeal.scala | 2 +- .../dotc/transform/ProtectedAccessors.scala | 2 +- .../tools/dotc/transform/SuperAccessors.scala | 10 ++--- .../dotty/tools/dotc/transform/TailRec.scala | 4 +- .../dotc/transform/TupleOptimizations.scala | 4 +- .../tools/dotc/transform/TypeTestsCasts.scala | 6 +-- .../transform/sjs/AddLocalJSFakeNews.scala | 2 +- .../transform/sjs/ExplicitJSClasses.scala | 4 +- .../transform/sjs/JUnitBootstrappers.scala | 3 +- .../dotc/transform/sjs/PrepJSExports.scala | 2 +- .../dotc/transform/sjs/PrepJSInterop.scala | 30 ++++++------- .../dotty/tools/dotc/typer/Applications.scala | 6 +-- .../src/dotty/tools/dotc/typer/Checking.scala | 20 ++++----- .../tools/dotc/typer/CrossVersionChecks.scala | 7 +-- .../src/dotty/tools/dotc/typer/Deriving.scala | 6 +-- .../dotty/tools/dotc/typer/Docstrings.scala | 2 +- .../dotty/tools/dotc/typer/Implicits.scala | 16 +++---- .../src/dotty/tools/dotc/typer/Namer.scala | 16 ++++--- .../tools/dotc/typer/QuotesAndSplices.scala | 14 +++--- .../dotty/tools/dotc/typer/RefChecks.scala | 18 ++++---- .../dotty/tools/dotc/typer/Synthesizer.scala | 13 +++--- .../dotty/tools/dotc/typer/TypeAssigner.scala | 7 +-- .../src/dotty/tools/dotc/typer/Typer.scala | 34 +++++++------- .../tools/dotc/typer/VarianceChecker.scala | 4 +- 59 files changed, 252 insertions(+), 233 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala index 3e2a8f1b0b60..bf10e37943a8 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala @@ -23,6 +23,7 @@ import dotty.tools.dotc.transform.SymUtils._ import dotty.tools.dotc.util.Spans._ import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.Phases._ +import dotty.tools.dotc.core.Decorators.em import dotty.tools.dotc.report /* @@ -700,7 +701,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { var elemKind = arr.elementType val argsSize = args.length if (argsSize > dims) { - report.error(s"too many arguments for array constructor: found ${args.length} but array has only $dims dimension(s)", ctx.source.atSpan(app.span)) + report.error(em"too many arguments for array constructor: found ${args.length} but array has only $dims dimension(s)", ctx.source.atSpan(app.span)) } if (argsSize < dims) { /* In one step: diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala b/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala index 33da88170ee1..3cf7d88b9282 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala @@ -79,7 +79,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { outputDirectory } catch { case ex: Throwable => - report.error(s"Couldn't create file for class $cName\n${ex.getMessage}", ctx.source.atSpan(csym.span)) + report.error(em"Couldn't create file for class $cName\n${ex.getMessage}", ctx.source.atSpan(csym.span)) null } } @@ -871,10 +871,11 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { try body catch { case ex: Throwable => - report.error(i"""|compiler bug: created invalid generic signature for $sym in ${sym.denot.owner.showFullName} - |signature: $sig - |if this is reproducible, please report bug at https://github.com/lampepfl/dotty/issues - """.trim, sym.sourcePos) + report.error( + em"""|compiler bug: created invalid generic signature for $sym in ${sym.denot.owner.showFullName} + |signature: $sig + |if this is reproducible, please report bug at https://github.com/lampepfl/dotty/issues + """, sym.sourcePos) throw ex } } diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala index a524d5fb5a8b..1d8a9c579cb9 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala @@ -748,7 +748,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { if (params.size > MaximumJvmParameters) { // SI-7324 - report.error(s"Platform restriction: a parameter list's length cannot exceed $MaximumJvmParameters.", ctx.source.atSpan(methSymbol.span)) + report.error(em"Platform restriction: a parameter list's length cannot exceed $MaximumJvmParameters.", ctx.source.atSpan(methSymbol.span)) return } @@ -800,9 +800,10 @@ trait BCodeSkelBuilder extends BCodeHelpers { val veryFirstProgramPoint = currProgramPoint() if trimmedRhs == tpd.EmptyTree then - report.error("Concrete method has no definition: " + dd + ( - if (ctx.settings.Ydebug.value) "(found: " + methSymbol.owner.info.decls.toList.mkString(", ") + ")" - else ""), + report.error( + em"Concrete method has no definition: $dd${ + if (ctx.settings.Ydebug.value) "(found: " + methSymbol.owner.info.decls.toList.mkString(", ") + ")" + else ""}", ctx.source.atSpan(NoSpan) ) else diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 5461ff81341c..ecdd0ae98803 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -14,6 +14,7 @@ import Contexts._ import Types._ import Symbols._ import Phases._ +import Decorators.em import dotty.tools.dotc.util.ReadOnlyMap import dotty.tools.dotc.report @@ -71,7 +72,7 @@ class DottyBackendInterface(val outputDirectory: AbstractFile, val superCallsMap def _1: Type = field.tpe match { case JavaArrayType(elem) => elem case _ => - report.error(s"JavaSeqArray with type ${field.tpe} reached backend: $field", ctx.source.atSpan(field.span)) + report.error(em"JavaSeqArray with type ${field.tpe} reached backend: $field", ctx.source.atSpan(field.span)) UnspecifiedErrorType } def _2: List[Tree] = field.elems diff --git a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala index 73e8fd9edb3b..e788c2b2a4ec 100644 --- a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala +++ b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala @@ -21,6 +21,7 @@ import dotty.tools.dotc.sbt.ExtractDependencies import Contexts._ import Phases._ import Symbols._ +import Decorators.em import java.io.DataOutputStream import java.nio.channels.ClosedByInterruptException @@ -308,7 +309,7 @@ class GenBCodePipeline(val int: DottyBackendInterface, val primitives: DottyPrim getFileForClassfile(outF, cls.name, ".class") } catch { case e: FileConflictException => - report.error(s"error writing ${cls.name}: ${e.getMessage}") + report.error(em"error writing ${cls.name}: ${e.getMessage}") null } } else null @@ -608,11 +609,11 @@ class GenBCodePipeline(val int: DottyBackendInterface, val primitives: DottyPrim val method = s"${e.getClassName.replaceAll("/", ".")}.${e.getMethodName}" val msg = - s"Generated bytecode for method '$method' is too large. Size: ${e.getCodeSize} bytes. Limit is 64KB" + em"Generated bytecode for method '$method' is too large. Size: ${e.getCodeSize} bytes. Limit is 64KB" report.error(msg) case e: ClassTooLargeException => val msg = - s"Class '${e.getClassName.replaceAll("/", ".")}' is too large. Constant pool size: ${e.getConstantPoolCount}. Limit is 64K entries" + em"Class '${e.getClassName.replaceAll("/", ".")}' is too large. Constant pool size: ${e.getConstantPoolCount}. Limit is 64K entries" report.error(msg) } diff --git a/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala b/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala index 2d4c3ce5c9c4..bc453aec17af 100644 --- a/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala +++ b/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala @@ -8,6 +8,7 @@ import Contexts._ import Names.TermName, StdNames._ import Types.{JavaArrayType, UnspecifiedErrorType, Type} import Symbols.{Symbol, NoSymbol} +import Decorators.em import dotc.report import dotc.util.ReadOnlyMap @@ -66,7 +67,7 @@ class DottyPrimitives(ictx: Context) { case defn.ArrayOf(el) => el case JavaArrayType(el) => el case _ => - report.error(s"expected Array $tpe") + report.error(em"expected Array $tpe") UnspecifiedErrorType } @@ -133,7 +134,7 @@ class DottyPrimitives(ictx: Context) { def addPrimitives(cls: Symbol, method: TermName, code: Int)(using Context): Unit = { val alts = cls.info.member(method).alternatives.map(_.symbol) if (alts.isEmpty) - report.error(s"Unknown primitive method $cls.$method") + report.error(em"Unknown primitive method $cls.$method") else alts foreach (s => addPrimitive(s, s.info.paramInfoss match { diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index 45a1cf8d3758..b9d63c139e3b 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -3652,7 +3652,7 @@ class JSCodeGen()(using genCtx: Context) { } else if (sym.isJSType) { if (sym.is(Trait)) { report.error( - s"isInstanceOf[${sym.fullName}] not supported because it is a JS trait", + em"isInstanceOf[${sym.fullName}] not supported because it is a JS trait", pos) js.BooleanLiteral(true) } else { diff --git a/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala b/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala index cac81c04ea93..a2807f41191a 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala @@ -308,7 +308,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) { if (isProp && methodSyms.nonEmpty) { val firstAlt = alts.head report.error( - i"Conflicting properties and methods for ${classSym.fullName}::$name.", + em"Conflicting properties and methods for ${classSym.fullName}::$name.", firstAlt.srcPos) implicit val pos = firstAlt.span js.JSPropertyDef(js.MemberFlags.empty, genExpr(name)(firstAlt.sourcePos), None, None) @@ -612,7 +612,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) { val altsTypesInfo = alts.map(_.info.show).sorted.mkString("\n ") report.error( - s"Cannot disambiguate overloads for $fullKind $displayName with types\n $altsTypesInfo", + em"Cannot disambiguate overloads for $fullKind $displayName with types\n $altsTypesInfo", pos) } diff --git a/compiler/src/dotty/tools/backend/sjs/JSPositions.scala b/compiler/src/dotty/tools/backend/sjs/JSPositions.scala index 9b19e66058e8..2fd007165952 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSPositions.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSPositions.scala @@ -6,6 +6,7 @@ import java.net.{URI, URISyntaxException} import dotty.tools.dotc.core._ import Contexts._ +import Decorators.em import dotty.tools.dotc.report @@ -31,7 +32,7 @@ class JSPositions()(using Context) { URIMap(from, to) :: Nil } catch { case e: URISyntaxException => - report.error(s"${e.getInput} is not a valid URI") + report.error(em"${e.getInput} is not a valid URI") Nil } } diff --git a/compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala b/compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala index 6b3854ed677f..dda018f8bb39 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala @@ -5,6 +5,7 @@ import Names.TermName import Types._ import Contexts._ import Symbols._ +import Decorators.em import dotty.tools.dotc.ast.tpd._ import dotty.tools.backend.jvm.DottyPrimitives @@ -90,7 +91,7 @@ class JSPrimitives(ictx: Context) extends DottyPrimitives(ictx) { def addPrimitives(cls: Symbol, method: TermName, code: Int)(using Context): Unit = { val alts = cls.info.member(method).alternatives.map(_.symbol) if (alts.isEmpty) { - report.error(s"Unknown primitive method $cls.$method") + report.error(em"Unknown primitive method $cls.$method") } else { for (s <- alts) addPrimitive(s, code) diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index 44ca582c3c61..ef7728387406 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -131,11 +131,11 @@ object CompilationUnit { if (!mustExist) source else if (source.file.isDirectory) { - report.error(s"expected file, received directory '${source.file.path}'") + report.error(em"expected file, received directory '${source.file.path}'") NoSource } else if (!source.file.exists) { - report.error(s"source file not found: ${source.file.path}") + report.error(em"source file not found: ${source.file.path}") NoSource } else source diff --git a/compiler/src/dotty/tools/dotc/Driver.scala b/compiler/src/dotty/tools/dotc/Driver.scala index 14a71463c66d..0af35dd4068a 100644 --- a/compiler/src/dotty/tools/dotc/Driver.scala +++ b/compiler/src/dotty/tools/dotc/Driver.scala @@ -94,7 +94,7 @@ class Driver { val newEntries: List[String] = files .flatMap { file => if !file.exists then - report.error(s"File does not exist: ${file.path}") + report.error(em"File does not exist: ${file.path}") None else file.extension match case "jar" => Some(file.path) @@ -102,10 +102,10 @@ class Driver { TastyFileUtil.getClassPath(file) match case Some(classpath) => Some(classpath) case _ => - report.error(s"Could not load classname from: ${file.path}") + report.error(em"Could not load classname from: ${file.path}") None case _ => - report.error(s"File extension is not `tasty` or `jar`: ${file.path}") + report.error(em"File extension is not `tasty` or `jar`: ${file.path}") None } .distinct diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 68f4b75e87de..59895d3c32a7 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -911,7 +911,7 @@ object desugar { case params :: paramss1 => // `params` must have a single parameter and without `given` flag def badRightAssoc(problem: String) = - report.error(i"right-associative extension method $problem", mdef.srcPos) + report.error(em"right-associative extension method $problem", mdef.srcPos) extParamss ++ mdef.paramss params match @@ -1237,7 +1237,7 @@ object desugar { def checkOpaqueAlias(tree: MemberDef)(using Context): MemberDef = def check(rhs: Tree): MemberDef = rhs match case bounds: TypeBoundsTree if bounds.alias.isEmpty => - report.error(i"opaque type must have a right-hand side", tree.srcPos) + report.error(em"opaque type must have a right-hand side", tree.srcPos) tree.withMods(tree.mods.withoutFlags(Opaque)) case LambdaTypeTree(_, body) => check(body) case _ => tree diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index 096a885dcf32..467bd8c7b420 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -216,7 +216,7 @@ object DesugarEnums { case Ident(name) => val matches = tparamNames.contains(name) if (matches && (caseTypeParams.nonEmpty || vparamss.isEmpty)) - report.error(i"illegal reference to type parameter $name from enum case", tree.srcPos) + report.error(em"illegal reference to type parameter $name from enum case", tree.srcPos) matches case LambdaTypeTree(lambdaParams, body) => underBinders(lambdaParams, foldOver(x, tree)) diff --git a/compiler/src/dotty/tools/dotc/ast/MainProxies.scala b/compiler/src/dotty/tools/dotc/ast/MainProxies.scala index 040582476e96..c0cf2c0d1b81 100644 --- a/compiler/src/dotty/tools/dotc/ast/MainProxies.scala +++ b/compiler/src/dotty/tools/dotc/ast/MainProxies.scala @@ -56,7 +56,7 @@ object MainProxies { def addArgs(call: untpd.Tree, mt: MethodType, idx: Int): untpd.Tree = if (mt.isImplicitMethod) { - report.error(s"@main method cannot have implicit parameters", pos) + report.error(em"@main method cannot have implicit parameters", pos) call } else { @@ -74,7 +74,7 @@ object MainProxies { mt.resType match { case restpe: MethodType => if (mt.paramInfos.lastOption.getOrElse(NoType).isRepeatedParam) - report.error(s"varargs parameter of @main method must come last", pos) + report.error(em"varargs parameter of @main method must come last", pos) addArgs(call1, restpe, idx + args.length) case _ => call1 @@ -83,7 +83,7 @@ object MainProxies { var result: List[TypeDef] = Nil if (!mainFun.owner.isStaticOwner) - report.error(s"@main method is not statically accessible", pos) + report.error(em"@main method is not statically accessible", pos) else { var call = ref(mainFun.termRef) mainFun.info match { @@ -91,9 +91,9 @@ object MainProxies { case mt: MethodType => call = addArgs(call, mt, 0) case _: PolyType => - report.error(s"@main method cannot have type parameters", pos) + report.error(em"@main method cannot have type parameters", pos) case _ => - report.error(s"@main can only annotate a method", pos) + report.error(em"@main can only annotate a method", pos) } val errVar = Ident(nme.error) val handler = CaseDef( @@ -203,7 +203,7 @@ object MainProxies { )) (sym, paramAnnotations.toVector, defaultValueSymbols(scope, sym), stat.rawComment) :: Nil case mainAnnot :: others => - report.error(s"method cannot have multiple main annotations", mainAnnot.tree) + report.error(em"method cannot have multiple main annotations", mainAnnot.tree) Nil } case stat @ TypeDef(_, impl: Template) if stat.symbol.is(Module) => @@ -379,26 +379,26 @@ object MainProxies { end generateMainClass if (!mainFun.owner.isStaticOwner) - report.error(s"main method is not statically accessible", pos) + report.error(em"main method is not statically accessible", pos) None else mainFun.info match { case _: ExprType => Some(generateMainClass(unitToValue(ref(mainFun.termRef)), Nil, Nil)) case mt: MethodType => if (mt.isImplicitMethod) - report.error(s"main method cannot have implicit parameters", pos) + report.error(em"main method cannot have implicit parameters", pos) None else mt.resType match case restpe: MethodType => - report.error(s"main method cannot be curried", pos) + report.error(em"main method cannot be curried", pos) None case _ => Some(generateMainClass(unitToValue(Apply(ref(mainFun.termRef), argRefs(mt))), argValDefs(mt), parameterInfos(mt))) case _: PolyType => - report.error(s"main method cannot have type parameters", pos) + report.error(em"main method cannot have type parameters", pos) None case _ => - report.error(s"main can only annotate a method", pos) + report.error(em"main can only annotate a method", pos) None } } diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 253477c5382c..1053e7c86780 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -1745,7 +1745,7 @@ object Trees { val denot = receiver.tpe.member(method) if !denot.exists then overload.println(i"members = ${receiver.tpe.decls}") - report.error(i"no member $receiver . $method", receiver.srcPos) + report.error(em"no member $receiver . $method", receiver.srcPos) val selected = if (denot.isOverloaded) { def typeParamCount(tp: Type) = tp.widen match { diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 3532e11ea3ef..eeea1dc0660d 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -203,7 +203,7 @@ class CheckCaptures extends Recheck, SymTransformer: def checkElem(elem: CaptureRef, cs: CaptureSet, pos: SrcPos)(using Context) = val res = elem.singletonCaptureSet.subCaptures(cs, frozen = false) if !res.isOK then - report.error(i"$elem cannot be referenced here; it is not included in the allowed capture set ${res.blocking}", pos) + report.error(em"$elem cannot be referenced here; it is not included in the allowed capture set ${res.blocking}", pos) /** Check subcapturing `cs1 <: cs2`, report error on failure */ def checkSubset(cs1: CaptureSet, cs2: CaptureSet, pos: SrcPos)(using Context) = @@ -212,7 +212,7 @@ class CheckCaptures extends Recheck, SymTransformer: def header = if cs1.elems.size == 1 then i"reference ${cs1.elems.toList}%, % is not" else i"references $cs1 are not all" - report.error(i"$header included in allowed capture set ${res.blocking}", pos) + report.error(em"$header included in allowed capture set ${res.blocking}", pos) /** The current environment */ private var curEnv: Env = Env(NoSymbol, nestedInOwner = false, CaptureSet.empty, isBoxed = false, null) @@ -866,9 +866,9 @@ class CheckCaptures extends Recheck, SymTransformer: // Forbid inferred self types unless they are already implied by an explicit // self type in a parent. report.error( - i"""$root needs an explicitly declared self type since its - |inferred self type $selfType - |is not visible in other compilation units that define subclasses.""", + em"""$root needs an explicitly declared self type since its + |inferred self type $selfType + |is not visible in other compilation units that define subclasses.""", root.srcPos) case _ => parentTrees -= root diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index e7117f542384..d11948dc4c37 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -129,7 +129,7 @@ object Feature: def checkExperimentalFeature(which: String, srcPos: SrcPos, note: => String = "")(using Context) = if !isExperimentalEnabled then - report.error(i"Experimental $which may only be used with a nightly or snapshot version of the compiler$note", srcPos) + report.error(em"Experimental $which may only be used with a nightly or snapshot version of the compiler$note", srcPos) def checkExperimentalDef(sym: Symbol, srcPos: SrcPos)(using Context) = if !isExperimentalEnabled then @@ -140,7 +140,7 @@ object Feature: i"${sym.owner} is marked @experimental" else i"$sym inherits @experimental" - report.error(s"$symMsg and therefore may only be used in an experimental scope.", srcPos) + report.error(em"$symMsg and therefore may only be used in an experimental scope.", srcPos) /** Check that experimental compiler options are only set for snapshot or nightly compiler versions. */ def checkExperimentalSettings(using Context): Unit = diff --git a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala index a2f697da1624..9eb67b468cfa 100644 --- a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -331,8 +331,9 @@ abstract class SymbolLoader extends LazyType { self => if (ctx.debug) ex.printStackTrace() val msg = ex.getMessage() report.error( - if (msg == null) "i/o error while loading " + root.name - else "error while loading " + root.name + ",\n" + msg) + if msg == null then em"i/o error while loading ${root.name}" + else em"""error while loading ${root.name}, + |$msg""") } try { val start = System.currentTimeMillis diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index d20391bce0a4..a47cf5515d2b 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -207,7 +207,7 @@ class TreePickler(pickler: TastyPickler) { else if (tpe.prefix == NoPrefix) { writeByte(if (tpe.isType) TYPEREFdirect else TERMREFdirect) if Config.checkLevelsOnConstraints && !symRefs.contains(sym) && !sym.isPatternBound && !sym.hasAnnotation(defn.QuotedRuntimePatterns_patternTypeAnnot) then - report.error(i"pickling reference to as yet undefined $tpe with symbol ${sym}", sym.srcPos) + report.error(em"pickling reference to as yet undefined $tpe with symbol ${sym}", sym.srcPos) pickleSymRef(sym) } else tpe.designator match { diff --git a/compiler/src/dotty/tools/dotc/fromtasty/TASTYRun.scala b/compiler/src/dotty/tools/dotc/fromtasty/TASTYRun.scala index 04c65a3d3882..fb0abe3332ed 100644 --- a/compiler/src/dotty/tools/dotc/fromtasty/TASTYRun.scala +++ b/compiler/src/dotty/tools/dotc/fromtasty/TASTYRun.scala @@ -6,6 +6,7 @@ import scala.language.unsafeNulls import io.{JarArchive, AbstractFile, Path} import core.Contexts._ +import core.Decorators.em import java.io.File class TASTYRun(comp: Compiler, ictx: Context) extends Run(comp, ictx) { @@ -27,7 +28,7 @@ class TASTYRun(comp: Compiler, ictx: Context) extends Run(comp, ictx) { .toList case "tasty" => TastyFileUtil.getClassName(file) case _ => - report.error(s"File extension is not `tasty` or `jar`: ${file.path}") + report.error(em"File extension is not `tasty` or `jar`: ${file.path}") Nil } classNames.map(new TASTYCompilationUnit(_)) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index bea42e82ce6f..92ca9b6ed724 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -253,7 +253,7 @@ class Inliner(val call: tpd.Tree)(using Context): computeParamBindings(tp.resultType, targs.drop(tp.paramNames.length), argss, formalss, buf) case tp: MethodType => if argss.isEmpty then - report.error(i"missing arguments for inline method $inlinedMethod", call.srcPos) + report.error(em"missing arguments for inline method $inlinedMethod", call.srcPos) false else tp.paramNames.lazyZip(formalss.head).lazyZip(argss.head).foreach { (name, formal, arg) => @@ -616,8 +616,8 @@ class Inliner(val call: tpd.Tree)(using Context): def issueError() = callValueArgss match { case (msgArg :: Nil) :: Nil => val message = msgArg.tpe match { - case ConstantType(Constant(msg: String)) => msg - case _ => s"A literal string is expected as an argument to `compiletime.error`. Got ${msgArg.show}" + case ConstantType(Constant(msg: String)) => msg.toMessage + case _ => em"A literal string is expected as an argument to `compiletime.error`. Got $msgArg" } // Usually `error` is called from within a rewrite method. In this // case we need to report the error at the point of the outermost enclosing inline diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 4e061bcb0b19..33cf1a2722c9 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1069,7 +1069,7 @@ object Parsers { val name = in.name if name == nme.CONSTRUCTOR || name == nme.STATIC_CONSTRUCTOR then report.error( - i"""Illegal backquoted identifier: `` and `` are forbidden""", + em"""Illegal backquoted identifier: `` and `` are forbidden""", in.sourcePos()) in.nextToken() name @@ -1352,9 +1352,9 @@ object Parsers { in.nextToken() if in.indentWidth(in.offset) == in.currentRegion.indentWidth then report.errorOrMigrationWarning( - i"""This opening brace will start a new statement in Scala 3. - |It needs to be indented to the right to keep being treated as - |an argument to the previous expression.${rewriteNotice()}""", + em"""This opening brace will start a new statement in Scala 3. + |It needs to be indented to the right to keep being treated as + |an argument to the previous expression.${rewriteNotice()}""", in.sourcePos(), from = `3.0`) patch(source, Span(in.offset), " ") @@ -1966,7 +1966,7 @@ object Parsers { } :: contextBounds(pname) else if in.token == VIEWBOUND then report.errorOrMigrationWarning( - "view bounds `<%' are no longer supported, use a context bound `:' instead", + em"view bounds `<%' are no longer supported, use a context bound `:' instead", in.sourcePos(), from = `3.0`) atSpan(in.skipToken()) { Function(Ident(pname) :: Nil, toplevelTyp()) @@ -2117,8 +2117,8 @@ object Parsers { } case DO => report.errorOrMigrationWarning( - i"""`do while ` is no longer supported, - |use `while ; do ()` instead.${rewriteNotice()}""", + em"""`do while ` is no longer supported, + |use `while ; do ()` instead.${rewriteNotice()}""", in.sourcePos(), from = `3.0`) val start = in.skipToken() atSpan(start) { @@ -2306,7 +2306,7 @@ object Parsers { val t = if ((in.token == COLONop || in.token == COLONfollow) && location == Location.InBlock) { report.errorOrMigrationWarning( - s"This syntax is no longer supported; parameter needs to be enclosed in (...)${rewriteNotice(`future-migration`)}", + em"This syntax is no longer supported; parameter needs to be enclosed in (...)${rewriteNotice(`future-migration`)}", source.atSpan(Span(start, in.lastOffset)), from = future) in.nextToken() @@ -3517,7 +3517,7 @@ object Parsers { else ": Unit " // trailing space ensures that `def f()def g()` works. if migrateTo3 then report.errorOrMigrationWarning( - s"Procedure syntax no longer supported; `$toInsert` should be inserted here", + em"Procedure syntax no longer supported; `$toInsert` should be inserted here", in.sourcePos(), from = `3.0`) patch(source, Span(in.lastOffset), toInsert) true @@ -3931,7 +3931,7 @@ object Parsers { in.nextToken() if (in.token == LBRACE || in.token == COLONeol) { report.errorOrMigrationWarning( - "`extends` must be followed by at least one parent", + em"`extends` must be followed by at least one parent", in.sourcePos(), from = `3.0`) Nil } diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index f1542003f620..8f9ac582bc12 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -100,6 +100,7 @@ object Scanners { */ var errOffset: Offset = NoOffset + /** Implements CharArrayReader's error method */ protected def error(msg: String, off: Offset): Unit = error(msg.toMessage, off) @@ -248,7 +249,7 @@ object Scanners { if scala3keywords.contains(keyword) && migrateTo3 then val what = tokenString(keyword) report.errorOrMigrationWarning( - i"$what is now a keyword, write `$what` instead of $what to keep it as an identifier", + em"$what is now a keyword, write `$what` instead of $what to keep it as an identifier", sourcePos(), from = `3.0`) patch(source, Span(offset), "`") @@ -978,11 +979,11 @@ object Scanners { def fetchOther() = if (ch == '\u21D2') { nextChar(); token = ARROW - report.deprecationWarning("The unicode arrow `⇒` is deprecated, use `=>` instead. If you still wish to display it as one character, consider using a font with programming ligatures such as Fira Code.", sourcePos(offset)) + report.deprecationWarning(em"The unicode arrow `⇒` is deprecated, use `=>` instead. If you still wish to display it as one character, consider using a font with programming ligatures such as Fira Code.", sourcePos(offset)) } else if (ch == '\u2190') { nextChar(); token = LARROW - report.deprecationWarning("The unicode arrow `←` is deprecated, use `<-` instead. If you still wish to display it as one character, consider using a font with programming ligatures such as Fira Code.", sourcePos(offset)) + report.deprecationWarning(em"The unicode arrow `←` is deprecated, use `<-` instead. If you still wish to display it as one character, consider using a font with programming ligatures such as Fira Code.", sourcePos(offset)) } else if (Character.isUnicodeIdentifierStart(ch)) { putChar(ch) diff --git a/compiler/src/dotty/tools/dotc/plugins/Plugins.scala b/compiler/src/dotty/tools/dotc/plugins/Plugins.scala index 3093a1c0460f..976b783c40f0 100644 --- a/compiler/src/dotty/tools/dotc/plugins/Plugins.scala +++ b/compiler/src/dotty/tools/dotc/plugins/Plugins.scala @@ -5,6 +5,7 @@ import scala.language.unsafeNulls import core._ import Contexts._ +import Decorators.em import config.{ PathResolver, Feature } import dotty.tools.io._ import Phases._ @@ -83,14 +84,14 @@ trait Plugins { // Verify required plugins are present. for (req <- ctx.settings.require.value ; if !(plugs exists (_.name == req))) - report.error("Missing required plugin: " + req) + report.error(em"Missing required plugin: $req") // Verify no non-existent plugin given with -P for { opt <- ctx.settings.pluginOptions.value if !(plugs exists (opt startsWith _.name + ":")) } - report.error("bad option: -P:" + opt) + report.error(em"bad option: -P:$opt") plugs } diff --git a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala index e205bef42b86..6c3d03fad4ca 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala @@ -47,7 +47,7 @@ abstract class Interpreter(pos: SrcPos, classLoader: ClassLoader)(using Context) case obj: T => Some(obj) case obj => // TODO upgrade to a full type tag check or something similar - report.error(s"Interpreted tree returned a result of an unexpected type. Expected ${ct.runtimeClass} but was ${obj.getClass}", pos) + report.error(em"Interpreted tree returned a result of an unexpected type. Expected ${ct.runtimeClass} but was ${obj.getClass}", pos) None } diff --git a/compiler/src/dotty/tools/dotc/report.scala b/compiler/src/dotty/tools/dotc/report.scala index 2c2155e9c162..fb2aa3863554 100644 --- a/compiler/src/dotty/tools/dotc/report.scala +++ b/compiler/src/dotty/tools/dotc/report.scala @@ -26,30 +26,18 @@ object report: def deprecationWarning(msg: Message, pos: SrcPos)(using Context): Unit = issueWarning(new DeprecationWarning(msg, pos.sourcePos)) - def deprecationWarning(msg: => String, pos: SrcPos)(using Context): Unit = - deprecationWarning(msg.toMessage, pos) - def migrationWarning(msg: Message, pos: SrcPos)(using Context): Unit = issueWarning(new MigrationWarning(msg, pos.sourcePos)) - def migrationWarning(msg: => String, pos: SrcPos)(using Context): Unit = - migrationWarning(msg.toMessage, pos) - def uncheckedWarning(msg: Message, pos: SrcPos)(using Context): Unit = issueWarning(new UncheckedWarning(msg, pos.sourcePos)) - def uncheckedWarning(msg: => String, pos: SrcPos)(using Context): Unit = - uncheckedWarning(msg.toMessage, pos) - def featureWarning(msg: Message, pos: SrcPos)(using Context): Unit = issueWarning(new FeatureWarning(msg, pos.sourcePos)) - def featureWarning(msg: => String, pos: SrcPos)(using Context): Unit = - featureWarning(msg.toMessage, pos) - def featureWarning(feature: String, featureDescription: => String, - featureUseSite: Symbol, required: Boolean, pos: SrcPos)(using Context): Unit = { - val req = if (required) "needs to" else "should" + featureUseSite: Symbol, required: Boolean, pos: SrcPos)(using Context): Unit = + val req = if required then "needs to" else "should" val fqname = s"scala.language.$feature" val explain = @@ -60,12 +48,12 @@ object report: |See the Scala docs for value $fqname for a discussion |why the feature $req be explicitly enabled.""".stripMargin - def msg = s"""$featureDescription $req be enabled - |by adding the import clause 'import $fqname' - |or by setting the compiler option -language:$feature.$explain""".stripMargin - if (required) error(msg, pos) - else issueWarning(new FeatureWarning(msg.toMessage, pos.sourcePos)) - } + def msg = em"""$featureDescription $req be enabled + |by adding the import clause 'import $fqname' + |or by setting the compiler option -language:$feature.$explain""" + if required then error(msg, pos) + else issueWarning(new FeatureWarning(msg, pos.sourcePos)) + end featureWarning def warning(msg: Message, pos: SrcPos)(using Context): Unit = issueWarning(new Warning(msg, addInlineds(pos))) @@ -76,17 +64,17 @@ object report: def warning(msg: => String, pos: SrcPos = NoSourcePosition)(using Context): Unit = warning(msg.toMessage, pos) - def error(msg: Message, pos: SrcPos)(using Context): Unit = + def error(msg: Message, pos: SrcPos = NoSourcePosition)(using Context): Unit = val fullPos = addInlineds(pos) ctx.reporter.report(new Error(msg, fullPos)) if ctx.settings.YdebugError.value then Thread.dumpStack() - def error(msg: Message)(using Context): Unit = - error(msg, NoSourcePosition) - - def error(msg: => String, pos: SrcPos = NoSourcePosition)(using Context): Unit = + def error(msg: => String, pos: SrcPos)(using Context): Unit = error(msg.toMessage, pos) + def error(msg: => String)(using Context): Unit = + error(msg, NoSourcePosition) + def error(ex: TypeError, pos: SrcPos)(using Context): Unit = val fullPos = addInlineds(pos) ctx.reporter.report(new StickyError(ex.toMessage, fullPos)) @@ -97,16 +85,10 @@ object report: if sourceVersion.isMigrating && sourceVersion.ordinal <= from.ordinal then migrationWarning(msg, pos) else error(msg, pos) - def errorOrMigrationWarning(msg: => String, pos: SrcPos, from: SourceVersion)(using Context): Unit = - errorOrMigrationWarning(msg.toMessage, pos, from) - def gradualErrorOrMigrationWarning(msg: Message, pos: SrcPos, warnFrom: SourceVersion, errorFrom: SourceVersion)(using Context): Unit = if sourceVersion.isAtLeast(errorFrom) then errorOrMigrationWarning(msg, pos, errorFrom) else if sourceVersion.isAtLeast(warnFrom) then warning(msg, pos) - def gradualErrorOrMigrationWarning(msg: => String, pos: SrcPos, warnFrom: SourceVersion, errorFrom: SourceVersion)(using Context): Unit = - gradualErrorOrMigrationWarning(msg.toMessage, pos, warnFrom, errorFrom) - def restrictionError(msg: Message, pos: SrcPos = NoSourcePosition)(using Context): Unit = error(msg.mapMsg("Implementation restriction: " + _), pos) diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index 03e4d2dbdfac..9e8d7289801b 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -13,6 +13,23 @@ import scala.language.unsafeNulls import scala.annotation.threadUnsafe +/** ## Tips for error message generation + * + * - You can use the `em` interpolator for error messages. It's defined in decorators. + * - You can also use a simple string argument for `error` or `warning` (not for the other variants), + * but the string should not be interpolated or composed of objects that require a + * Context for evaluation. + * - When embedding interpolated substrings defined elsewhere in error messages, + * make sure they are defined as def's instead of vals. That way, the + * possibly expensive interpolation will performed only in the case where the message + * is eventually printed. Note: At least during typer, it's common for messages + * to be discarded without being printed. Also, by making them defs, you ensure that + * they will be evaluated in the Message context, which makes formatting safer + * and more robust. + * - For common messages, or messages that might require explanation, prefer defining + * a new Message class in messages and use that instead. The advantage is that these + * messages have unique IDs that can be referenced elsewhere. + */ object Message: def rewriteNotice(what: String, version: SourceVersion | Null = null, options: String = "")(using Context): String = if !ctx.mode.is(Mode.Interactive) then diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala index f7b15dc21eb0..e1b3d11a46ec 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala @@ -190,7 +190,7 @@ object ExtractDependencies { /** Report an internal error in incremental compilation. */ def internalError(msg: => String, pos: SrcPos = NoSourcePosition)(using Context): Unit = - report.error(s"Internal error in the incremental compiler while compiling ${ctx.compilationUnit.source}: $msg", pos) + report.error(em"Internal error in the incremental compiler while compiling ${ctx.compilationUnit.source}: $msg", pos) } private case class ClassDependency(from: Symbol, to: Symbol, context: DependencyContext) diff --git a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala index be454281bcbb..80b72075282f 100644 --- a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala +++ b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala @@ -80,7 +80,7 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => parents.map { case app @ Apply(fn, args0) if fn.symbol.owner == targetCls => if args0.nonEmpty && targetCls == defn.JavaEnumClass then - report.error("the constructor of java.lang.Enum cannot be called explicitly", app.sourcePos) + report.error(em"the constructor of java.lang.Enum cannot be called explicitly", app.sourcePos) cpy.Apply(app)(fn, args0 ++ args) case p => p } diff --git a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala index bdc2a268c1f8..78baec70bee6 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala @@ -51,10 +51,10 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase => // see https://github.com/scala/bug/issues/11714 val validJava = isValidJavaVarArgs(sym.info) if !validJava then - report.error("""To generate java-compatible varargs: + report.error(em"""To generate java-compatible varargs: | - there must be a single repeated parameter | - it must be the last argument in the last parameter list - |""".stripMargin, + |""", sym.sourcePos) else addVarArgsForwarder(sym, isJavaVarargsOverride, hasAnnotation, parentHasAnnotation) diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 7ba1f881d6d0..557b2ac5a4c6 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -549,8 +549,8 @@ object Erasure { /** Check that Java statics and packages can only be used in selections. */ - private def checkNotErased(tree: Tree)(using Context): tree.type = { - if (!ctx.mode.is(Mode.Type)) { + private def checkNotErased(tree: Tree)(using Context): tree.type = + if !ctx.mode.is(Mode.Type) then if isErased(tree) then val msg = if tree.symbol.is(Flags.Inline) then @@ -560,18 +560,19 @@ object Erasure { else em"${tree.symbol} is declared as `erased`, but is in fact used" report.error(msg, tree.srcPos) - tree.symbol.getAnnotation(defn.CompileTimeOnlyAnnot) match { + tree.symbol.getAnnotation(defn.CompileTimeOnlyAnnot) match case Some(annot) => - def defaultMsg = - i"""Reference to ${tree.symbol.showLocated} should not have survived, - |it should have been processed and eliminated during expansion of an enclosing macro or term erasure.""" - val message = annot.argumentConstant(0).fold(defaultMsg)(_.stringValue) + val message = annot.argumentConstant(0) match + case Some(c) => + c.stringValue.toMessage + case _ => + em"""Reference to ${tree.symbol.showLocated} should not have survived, + |it should have been processed and eliminated during expansion of an enclosing macro or term erasure.""" report.error(message, tree.srcPos) case _ => // OK - } - } + checkNotErasedClass(tree) - } + end checkNotErased private def checkNotErasedClass(tp: Type, tree: untpd.Tree)(using Context): Unit = tp match case JavaArrayType(et) => diff --git a/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala b/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala index cd6753eaed69..0552fe31f8a2 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala @@ -186,7 +186,7 @@ class ExpandSAMs extends MiniPhase: private def checkRefinements(tpe: Type, tree: Tree)(using Context): Type = tpe.dealias match { case RefinedType(parent, name, _) => if (name.isTermName && tpe.member(name).symbol.ownersIterator.isEmpty) // if member defined in the refinement - report.error("Lambda does not define " + name, tree.srcPos) + report.error(em"Lambda does not define $name", tree.srcPos) checkRefinements(parent, tree) case tpe => tpe diff --git a/compiler/src/dotty/tools/dotc/transform/InlineVals.scala b/compiler/src/dotty/tools/dotc/transform/InlineVals.scala index 65212ec2c0cc..047a187bad68 100644 --- a/compiler/src/dotty/tools/dotc/transform/InlineVals.scala +++ b/compiler/src/dotty/tools/dotc/transform/InlineVals.scala @@ -38,8 +38,8 @@ class InlineVals extends MiniPhase: tpt.tpe.widenTermRefExpr.dealiasKeepOpaques.normalized match case tp: ConstantType => if !isPureExpr(rhs) then - val details = if enclosingInlineds.isEmpty then "" else em"but was: $rhs" - report.error(s"inline value must be pure$details", rhs.srcPos) + def details = if enclosingInlineds.isEmpty then "" else i"but was: $rhs" + report.error(em"inline value must be pure$details", rhs.srcPos) case tp => if tp.typeSymbol.is(Opaque) then report.error(em"The type of an `inline val` cannot be an opaque type.\n\nTo inline, consider using `inline def` instead", rhs) diff --git a/compiler/src/dotty/tools/dotc/transform/NonLocalReturns.scala b/compiler/src/dotty/tools/dotc/transform/NonLocalReturns.scala index 7e1ae9e661f6..ddf858994220 100644 --- a/compiler/src/dotty/tools/dotc/transform/NonLocalReturns.scala +++ b/compiler/src/dotty/tools/dotc/transform/NonLocalReturns.scala @@ -6,6 +6,7 @@ import Contexts._, Symbols._, Types._, Flags._, StdNames._ import MegaPhase._ import NameKinds.NonLocalReturnKeyName import config.SourceVersion.* +import Decorators.em object NonLocalReturns { import ast.tpd._ @@ -96,7 +97,7 @@ class NonLocalReturns extends MiniPhase { override def transformReturn(tree: Return)(using Context): Tree = if isNonLocalReturn(tree) then report.gradualErrorOrMigrationWarning( - "Non local returns are no longer supported; use scala.util.control.NonLocalReturns instead", + em"Non local returns are no longer supported; use scala.util.control.NonLocalReturns instead", tree.srcPos, warnFrom = `3.2`, errorFrom = future) diff --git a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala index 137275a144a5..9e6c54b85304 100644 --- a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala @@ -253,7 +253,7 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( pos) tp case _ => - report.error(i"""Reference to $tp within quotes requires a given $reqType in scope. + report.error(em"""Reference to $tp within quotes requires a given $reqType in scope. | |""", pos) tp diff --git a/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala b/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala index 98e835293303..6d8f7bdb32cb 100644 --- a/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala +++ b/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala @@ -70,7 +70,7 @@ class ProtectedAccessors extends MiniPhase { override def ifNoHost(reference: RefTree)(using Context): Tree = { val curCls = ctx.owner.enclosingClass transforms.println(i"${curCls.ownersIterator.toList}%, %") - report.error(i"illegal access to protected ${reference.symbol.showLocated} from $curCls", + report.error(em"illegal access to protected ${reference.symbol.showLocated} from $curCls", reference.srcPos) reference } diff --git a/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala b/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala index b0c8605e7dd1..2307f759b571 100644 --- a/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -88,7 +88,7 @@ class SuperAccessors(thisPhase: DenotTransformer) { // Diagnostic for SI-7091 if (!accDefs.contains(clazz)) report.error( - s"Internal error: unable to store accessor definition in ${clazz}. clazz.hasPackageFlag=${clazz.is(Package)}. Accessor required for ${sel} (${sel.show})", + em"Internal error: unable to store accessor definition in ${clazz}. clazz.hasPackageFlag=${clazz.is(Package)}. Accessor required for ${sel.toString} ($sel)", sel.srcPos) else accDefs(clazz) += DefDef(acc, EmptyTree).withSpan(accRange) acc @@ -109,16 +109,16 @@ class SuperAccessors(thisPhase: DenotTransformer) { if (sym.isTerm && !sym.is(Method, butNot = Accessor) && !ctx.owner.isAllOf(ParamForwarder)) // ParamForwaders as installed ParamForwarding.scala do use super calls to vals - report.error(s"super may be not be used on ${sym.underlyingSymbol}", sel.srcPos) + report.error(em"super may be not be used on ${sym.underlyingSymbol}", sel.srcPos) else if (isDisallowed(sym)) - report.error(s"super not allowed here: use this.${sel.name} instead", sel.srcPos) + report.error(em"super not allowed here: use this.${sel.name} instead", sel.srcPos) else if (sym.is(Deferred)) { val member = sym.overridingSymbol(clazz.asClass) if (!mix.name.isEmpty || !member.exists || !(member.is(AbsOverride) && member.isIncompleteIn(clazz))) report.error( - i"${sym.showLocated} is accessed from super. It may not be abstract unless it is overridden by a member declared `abstract' and `override'", + em"${sym.showLocated} is accessed from super. It may not be abstract unless it is overridden by a member declared `abstract' and `override'", sel.srcPos) else report.log(i"ok super $sel ${sym.showLocated} $member $clazz ${member.isIncompleteIn(clazz)}") } @@ -131,7 +131,7 @@ class SuperAccessors(thisPhase: DenotTransformer) { val overriding = sym.overridingSymbol(intermediateClass) if (overriding.is(Deferred, butNot = AbsOverride) && !overriding.owner.is(Trait)) report.error( - s"${sym.showLocated} cannot be directly accessed from ${clazz} because ${overriding.owner} redeclares it as abstract", + em"${sym.showLocated} cannot be directly accessed from ${clazz} because ${overriding.owner} redeclares it as abstract", sel.srcPos) } else { diff --git a/compiler/src/dotty/tools/dotc/transform/TailRec.scala b/compiler/src/dotty/tools/dotc/transform/TailRec.scala index 71b66c3d0da6..741b9d1627fe 100644 --- a/compiler/src/dotty/tools/dotc/transform/TailRec.scala +++ b/compiler/src/dotty/tools/dotc/transform/TailRec.scala @@ -4,7 +4,7 @@ package transform import ast.{TreeTypeMap, tpd} import config.Printers.tailrec import core.* -import Contexts.*, Flags.*, Symbols.* +import Contexts.*, Flags.*, Symbols.*, Decorators.em import Constants.Constant import NameKinds.{TailLabelName, TailLocalName, TailTempName} import StdNames.nme @@ -303,7 +303,7 @@ class TailRec extends MiniPhase { def fail(reason: String) = { if (isMandatory) { failureReported = true - report.error(s"Cannot rewrite recursive call: $reason", tree.srcPos) + report.error(em"Cannot rewrite recursive call: $reason", tree.srcPos) } else tailrec.println("Cannot rewrite recursive call at: " + tree.span + " because: " + reason) diff --git a/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala b/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala index 6bc2f438eb37..6fba0bca4ce3 100644 --- a/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala +++ b/compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala @@ -145,7 +145,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer { val size = tpes.size val n = nTpe.value.intValue if (n < 0 || n >= size) { - report.error("index out of bounds: " + n, nTree.underlyingArgument.srcPos) + report.error(em"index out of bounds: $n", nTree.underlyingArgument.srcPos) tree } else if (size <= MaxTupleArity) @@ -155,7 +155,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer { // tup.asInstanceOf[TupleXXL].productElement(n) tup.asInstance(defn.TupleXXLClass.typeRef).select(nme.productElement).appliedTo(Literal(nTpe.value)) case (None, nTpe: ConstantType) if nTpe.value.intValue < 0 => - report.error("index out of bounds: " + nTpe.value.intValue, nTree.srcPos) + report.error(em"index out of bounds: ${nTpe.value.intValue}", nTree.srcPos) tree case _ => // No optimization, keep: diff --git a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index b2a101649457..3763af243881 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -241,7 +241,7 @@ object TypeTestsCasts { val foundEffectiveClass = effectiveClass(expr.tpe.widen) if foundEffectiveClass.isPrimitiveValueClass && !testCls.isPrimitiveValueClass then - report.error(i"cannot test if value of $exprType is a reference of $testCls", tree.srcPos) + report.error(em"cannot test if value of $exprType is a reference of $testCls", tree.srcPos) false else foundClasses.exists(check) end checkSensical @@ -345,7 +345,7 @@ object TypeTestsCasts { val testWidened = testType.widen defn.untestableClasses.find(testWidened.isRef(_)) match case Some(untestable) => - report.error(i"$untestable cannot be used in runtime type tests", tree.srcPos) + report.error(em"$untestable cannot be used in runtime type tests", tree.srcPos) constant(expr, Literal(Constant(false))) case _ => val erasedTestType = erasure(testType) @@ -359,7 +359,7 @@ object TypeTestsCasts { if !isTrusted && !isUnchecked then val whyNot = whyUncheckable(expr.tpe, argType, tree.span) if whyNot.nonEmpty then - report.uncheckedWarning(i"the type test for $argType cannot be checked at runtime because $whyNot", expr.srcPos) + report.uncheckedWarning(em"the type test for $argType cannot be checked at runtime because $whyNot", expr.srcPos) transformTypeTest(expr, argType, flagUnrelated = enclosingInlineds.isEmpty) // if test comes from inlined code, dont't flag it even if it always false } diff --git a/compiler/src/dotty/tools/dotc/transform/sjs/AddLocalJSFakeNews.scala b/compiler/src/dotty/tools/dotc/transform/sjs/AddLocalJSFakeNews.scala index 8851e641122f..6471e58d4ddc 100644 --- a/compiler/src/dotty/tools/dotc/transform/sjs/AddLocalJSFakeNews.scala +++ b/compiler/src/dotty/tools/dotc/transform/sjs/AddLocalJSFakeNews.scala @@ -65,7 +65,7 @@ class AddLocalJSFakeNews extends MiniPhase { thisPhase => constant.typeValue.typeSymbol.asClass case _ => // this shouldn't happen - report.error(i"unexpected $classValueArg for the first argument to `createLocalJSClass`", classValueArg) + report.error(em"unexpected $classValueArg for the first argument to `createLocalJSClass`", classValueArg) jsdefn.JSObjectClass } diff --git a/compiler/src/dotty/tools/dotc/transform/sjs/ExplicitJSClasses.scala b/compiler/src/dotty/tools/dotc/transform/sjs/ExplicitJSClasses.scala index 3c87621413b7..705b3cc404a8 100644 --- a/compiler/src/dotty/tools/dotc/transform/sjs/ExplicitJSClasses.scala +++ b/compiler/src/dotty/tools/dotc/transform/sjs/ExplicitJSClasses.scala @@ -651,7 +651,7 @@ class ExplicitJSClasses extends MiniPhase with InfoTransformer { thisPhase => case typeRef: TypeRef => typeRef case _ => // This should not have passed the checks in PrepJSInterop - report.error(i"class type required but found $tpe0", tree) + report.error(em"class type required but found $tpe0", tree) jsdefn.JSObjectType } val cls = tpe.typeSymbol @@ -667,7 +667,7 @@ class ExplicitJSClasses extends MiniPhase with InfoTransformer { thisPhase => val jsclassAccessor = jsclassAccessorFor(cls) ref(NamedType(prefix, jsclassAccessor.name, jsclassAccessor.denot)) } else { - report.error(i"stable reference to a JS class required but $tpe found", tree) + report.error(em"stable reference to a JS class required but $tpe found", tree) ref(defn.Predef_undefined) } } else if (isLocalJSClass(cls)) { diff --git a/compiler/src/dotty/tools/dotc/transform/sjs/JUnitBootstrappers.scala b/compiler/src/dotty/tools/dotc/transform/sjs/JUnitBootstrappers.scala index 817a6c5afabc..b911d7dfab96 100644 --- a/compiler/src/dotty/tools/dotc/transform/sjs/JUnitBootstrappers.scala +++ b/compiler/src/dotty/tools/dotc/transform/sjs/JUnitBootstrappers.scala @@ -13,6 +13,7 @@ import Scopes._ import Symbols._ import StdNames._ import Types._ +import Decorators.em import dotty.tools.dotc.transform.MegaPhase._ @@ -238,7 +239,7 @@ class JUnitBootstrappers extends MiniPhase { case NamedArg(name, _) => name.show(using ctx) case other => other.show(using ctx) } - report.error(s"$shownName is an unsupported argument for the JUnit @Test annotation in this position", other.sourcePos) + report.error(em"$shownName is an unsupported argument for the JUnit @Test annotation in this position", other.sourcePos) None } } diff --git a/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSExports.scala b/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSExports.scala index b0de197635e9..25ab46712e70 100644 --- a/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSExports.scala +++ b/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSExports.scala @@ -189,7 +189,7 @@ object PrepJSExports { if (hasExplicitName) { annot.argumentConstantString(0).getOrElse { report.error( - s"The argument to ${annot.symbol.name} must be a literal string", + em"The argument to ${annot.symbol.name} must be a literal string", annot.arguments(0)) "dummy" } diff --git a/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala b/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala index 48e6802e0f6c..d934dc179989 100644 --- a/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala +++ b/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala @@ -248,9 +248,9 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP if (tpeSym.isJSType) { def reportError(reasonAndExplanation: String): Unit = { report.error( - "Using an anonymous function as a SAM for the JavaScript type " + - i"${tpeSym.fullName} is not allowed because " + - reasonAndExplanation, + em"Using an anonymous function as a SAM for the JavaScript type ${ + tpeSym.fullName + } is not allowed because $reasonAndExplanation", tree) } if (!tpeSym.is(Trait) || tpeSym.asClass.superClass != jsdefn.JSFunctionClass) { @@ -318,9 +318,9 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP nameArgs match { case List(Literal(Constant(s: String))) => if (s != "apply") - report.error(i"js.Dynamic.literal does not have a method named $s", tree) + report.error(em"js.Dynamic.literal does not have a method named $s", tree) case _ => - report.error(i"js.Dynamic.literal.${tree.symbol.name} may not be called directly", tree) + report.error(em"js.Dynamic.literal.${tree.symbol.name} may not be called directly", tree) } // TODO Warn for known duplicate property names @@ -381,7 +381,7 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP tpe.underlyingClassRef(refinementOK = false) match { case typeRef: TypeRef if typeRef.symbol.isOneOf(Trait | ModuleClass) => - report.error(i"non-trait class type required but $tpe found", tpeArg) + report.error(em"non-trait class type required but $tpe found", tpeArg) case _ => // an error was already reported above } @@ -440,7 +440,7 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP * which is never valid. */ report.error( - i"${sym.name} extends ${parentSym.fullName} which does not extend js.Any.", + em"${sym.name} extends ${parentSym.fullName} which does not extend js.Any.", classDef) } } @@ -502,8 +502,8 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP def emitOverrideError(msg: String): Unit = { report.error( - "error overriding %s;\n %s %s".format( - infoStringWithLocation(overridden), infoString(overriding), msg), + em"""error overriding ${infoStringWithLocation(overridden)}; + | ${infoString(overriding)} $msg""", errorPos) } @@ -559,7 +559,7 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP for (annot <- sym.annotations) { val annotSym = annot.symbol if (isJSNativeLoadingSpecAnnot(annotSym)) - report.error(i"Traits may not have an @${annotSym.name} annotation.", annot.tree) + report.error(em"Traits may not have an @${annotSym.name} annotation.", annot.tree) } } else { checkJSNativeLoadSpecOf(treePos, sym) @@ -571,7 +571,7 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP def checkGlobalRefName(globalRef: String): Unit = { if (!JSGlobalRef.isValidJSGlobalRefName(globalRef)) - report.error(s"The name of a JS global variable must be a valid JS identifier (got '$globalRef')", pos) + report.error(em"The name of a JS global variable must be a valid JS identifier (got '$globalRef')", pos) } if (enclosingOwner is OwnerKind.JSNative) { @@ -585,7 +585,7 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP for (annot <- sym.annotations) { val annotSym = annot.symbol if (isJSNativeLoadingSpecAnnot(annotSym)) - report.error(i"Nested JS classes and objects cannot have an @${annotSym.name} annotation.", annot.tree) + report.error(em"Nested JS classes and objects cannot have an @${annotSym.name} annotation.", annot.tree) } if (sym.owner.isStaticOwner) { @@ -731,7 +731,7 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP if (overriddenSymbols.hasNext) { val overridden = overriddenSymbols.next() val verb = if (overridden.is(Deferred)) "implement" else "override" - report.error(i"An @js.native member cannot $verb the inherited member ${overridden.fullName}", tree) + report.error(em"An @js.native member cannot $verb the inherited member ${overridden.fullName}", tree) } tree @@ -984,7 +984,7 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP // Check that the result type was explicitly specified // (This is stronger than Scala 2, which only warns, and only if it was inferred as Nothing.) if (tree.tpt.isInstanceOf[InferredTypeTree]) - report.error(i"The type of ${tree.name} must be explicitly specified because it is JS native.", tree) + report.error(em"The type of ${tree.name} must be explicitly specified because it is JS native.", tree) } private def checkJSNativeSpecificAnnotsOnNonJSNative(memberDef: MemberDef)(using Context): Unit = { @@ -1321,7 +1321,7 @@ object PrepJSInterop { for (annotation <- sym.annotations) { if (isCompilerAnnotation(annotation)) { report.error( - i"@${annotation.symbol.fullName} is for compiler internal use only. Do not use it yourself.", + em"@${annotation.symbol.fullName} is for compiler internal use only. Do not use it yourself.", annotation.tree) } } diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 94cd39b89bcd..29c104ccba0a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1104,8 +1104,8 @@ trait Applications extends Compatibility { for (case arg @ NamedArg(id, argtpt) <- args) yield { if !Feature.namedTypeArgsEnabled then report.error( - i"""Named type arguments are experimental, - |they must be enabled with a `experimental.namedTypeArguments` language import or setting""", + em"""Named type arguments are experimental, + |they must be enabled with a `experimental.namedTypeArguments` language import or setting""", arg.srcPos) val argtpt1 = typedType(argtpt) cpy.NamedArg(arg)(id, argtpt1).withType(argtpt1.tpe) @@ -1396,7 +1396,7 @@ trait Applications extends Compatibility { case Apply(Apply(unapply, `dummyArg` :: Nil), args2) => assert(args2.nonEmpty); res ++= args2 case Apply(unapply, `dummyArg` :: Nil) => case Inlined(u, _, _) => loop(u) - case DynamicUnapply(_) => report.error("Structural unapply is not supported", unapplyFn.srcPos) + case DynamicUnapply(_) => report.error(em"Structural unapply is not supported", unapplyFn.srcPos) case Apply(fn, args) => assert(args.nonEmpty); loop(fn); res ++= args case _ => ().assertingErrorsReported } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 5e4f86f6420f..4e35152667c2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -1023,8 +1023,8 @@ trait Checking { ("method", (n: Name) => s"method syntax .$n(...)") def rewriteMsg = Message.rewriteNotice("The latter", options = "-deprecation") report.deprecationWarning( - i"""Alphanumeric $kind $name is not declared ${hlAsKeyword("infix")}; it should not be used as infix operator. - |Instead, use ${alternative(name)} or backticked identifier `$name`.$rewriteMsg""", + em"""Alphanumeric $kind $name is not declared ${hlAsKeyword("infix")}; it should not be used as infix operator. + |Instead, use ${alternative(name)} or backticked identifier `$name`.$rewriteMsg""", tree.op.srcPos) if (ctx.settings.deprecation.value) { patch(Span(tree.op.span.start, tree.op.span.start), "`") @@ -1057,7 +1057,7 @@ trait Checking { } tp match { case tp @ AndType(tp1, tp2) => - report.error(s"conflicting type arguments$where", pos) + report.error(em"conflicting type arguments$where", pos) tp1 case tp @ AppliedType(tycon, args) => tp.derivedAppliedType(tycon, args.mapConserve(checkGoodBounds)) @@ -1112,11 +1112,11 @@ trait Checking { if (!ctx.isAfterTyper) { val called = call.tpe.classSymbol if (called.is(JavaAnnotation)) - report.error(i"${called.name} must appear without any argument to be a valid class parent because it is a Java annotation", call.srcPos) + report.error(em"${called.name} must appear without any argument to be a valid class parent because it is a Java annotation", call.srcPos) if (caller.is(Trait)) - report.error(i"$caller may not call constructor of $called", call.srcPos) + report.error(em"$caller may not call constructor of $called", call.srcPos) else if (called.is(Trait) && !caller.mixins.contains(called)) - report.error(i"""$called is already implemented by super${caller.superClass}, + report.error(em"""$called is already implemented by super${caller.superClass}, |its constructor cannot be called again""", call.srcPos) // Check that constructor call is of the form _.(args1)...(argsN). @@ -1125,7 +1125,7 @@ trait Checking { case Apply(fn, _) => checkLegalConstructorCall(fn, tree, "") case TypeApply(fn, _) => checkLegalConstructorCall(fn, tree, "type ") case Select(_, nme.CONSTRUCTOR) => // ok - case _ => report.error(s"too many ${kind}arguments in parent constructor", encl.srcPos) + case _ => report.error(em"too many ${kind}arguments in parent constructor", encl.srcPos) } call match { case Apply(fn, _) => checkLegalConstructorCall(fn, call, "") @@ -1189,7 +1189,7 @@ trait Checking { val check = new TreeTraverser { def traverse(tree: Tree)(using Context) = tree match { case id: Ident if vparams.exists(_.symbol == id.symbol) => - report.error("illegal forward reference to method parameter", id.srcPos) + report.error(em"illegal forward reference to method parameter", id.srcPos) case _ => traverseChildren(tree) } @@ -1232,7 +1232,7 @@ trait Checking { if (t.span.isSourceDerived && owner == badOwner) t match { case t: RefTree if allowed(t.name, checkedSym) => - case _ => report.error(i"illegal reference to $checkedSym from $where", t.srcPos) + case _ => report.error(em"illegal reference to $checkedSym from $where", t.srcPos) } val sym = t.symbol t match { @@ -1349,7 +1349,7 @@ trait Checking { def ensureParentDerivesFrom(enumCase: Symbol)(using Context) = val enumCls = enumCase.owner.linkedClass if !firstParent.derivesFrom(enumCls) then - report.error(i"enum case does not extend its enum $enumCls", enumCase.srcPos) + report.error(em"enum case does not extend its enum $enumCls", enumCase.srcPos) cls.info match case info: ClassInfo => cls.info = info.derivedClassInfo(declaredParents = enumCls.typeRefApplied :: info.declaredParents) diff --git a/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala b/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala index 746b01c934a3..ef9599be551c 100644 --- a/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala @@ -67,7 +67,7 @@ class CrossVersionChecks extends MiniPhase: if !skipWarning then val msg = annot.argumentConstant(0).map(": " + _.stringValue).getOrElse("") val since = annot.argumentConstant(1).map(" since " + _.stringValue).getOrElse("") - report.deprecationWarning(s"${sym.showLocated} is deprecated${since}${msg}", pos) + report.deprecationWarning(em"${sym.showLocated} is deprecated${since}${msg}", pos) private def checkExperimentalSignature(sym: Symbol, pos: SrcPos)(using Context): Unit = class Checker extends TypeTraverser: @@ -110,8 +110,9 @@ class CrossVersionChecks extends MiniPhase: !sym.isDeprecated && !sym.is(Deferred)) if (!concrOvers.isEmpty) report.deprecationWarning( - symbol.toString + " overrides concrete, non-deprecated symbol(s):" + - concrOvers.map(_.name).mkString(" ", ", ", ""), tree.srcPos) + em"""$symbol overrides concrete, non-deprecated definition(s): + | ${concrOvers.map(_.name).mkString(", ")}""", + tree.srcPos) } } diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index d2165a5ca8c5..8fdc468780ba 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -44,7 +44,7 @@ trait Deriving { private def addDerivedInstance(clsName: Name, info: Type, pos: SrcPos): Unit = { val instanceName = "derived$".concat(clsName) if (ctx.denotNamed(instanceName).exists) - report.error(i"duplicate type class derivation for $clsName", pos) + report.error(em"duplicate type class derivation for $clsName", pos) else // If we set the Synthetic flag here widenGiven will widen too far and the // derived instance will have too low a priority to be selected over a freshly @@ -90,7 +90,7 @@ trait Deriving { xs.corresponds(ys)((x, y) => x.paramInfo.hasSameKindAs(y.paramInfo)) def cannotBeUnified = - report.error(i"${cls.name} cannot be unified with the type argument of ${typeClass.name}", derived.srcPos) + report.error(em"${cls.name} cannot be unified with the type argument of ${typeClass.name}", derived.srcPos) def addInstance(derivedParams: List[TypeSymbol], evidenceParamInfos: List[List[Type]], instanceTypes: List[Type]): Unit = { val resultType = typeClassType.appliedTo(instanceTypes) @@ -252,7 +252,7 @@ trait Deriving { if (typeClassArity == 1) deriveSingleParameter else if (typeClass == defn.CanEqualClass) deriveCanEqual else if (typeClassArity == 0) - report.error(i"type ${typeClass.name} in derives clause of ${cls.name} has no type parameters", derived.srcPos) + report.error(em"type ${typeClass.name} in derives clause of ${cls.name} has no type parameters", derived.srcPos) else cannotBeUnified } diff --git a/compiler/src/dotty/tools/dotc/typer/Docstrings.scala b/compiler/src/dotty/tools/dotc/typer/Docstrings.scala index 5fefd355d7d8..d819528ff556 100644 --- a/compiler/src/dotty/tools/dotc/typer/Docstrings.scala +++ b/compiler/src/dotty/tools/dotc/typer/Docstrings.scala @@ -37,7 +37,7 @@ object Docstrings { case List(df: tpd.DefDef) => usecase.typed(df) case _ => - report.error("`@usecase` was not a valid definition", ctx.source.atSpan(usecase.codePos)) + report.error(em"`@usecase` was not a valid definition", ctx.source.atSpan(usecase.codePos)) usecase } } diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 41d4bef5e81c..3e14292419a5 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -846,7 +846,7 @@ trait Implicits: inferred match { case SearchSuccess(_, ref, _, false) if isOldStyleFunctionConversion(ref.underlying) => report.migrationWarning( - i"The conversion ${ref} will not be applied implicitly here in Scala 3 because only implicit methods and instances of Conversion class will continue to work as implicit views.", + em"The conversion ${ref} will not be applied implicitly here in Scala 3 because only implicit methods and instances of Conversion class will continue to work as implicit views.", from ) case _ => @@ -1352,13 +1352,13 @@ trait Implicits: def warnAmbiguousNegation(ambi: AmbiguousImplicits) = report.migrationWarning( - i"""Ambiguous implicits ${ambi.alt1.ref.symbol.showLocated} and ${ambi.alt2.ref.symbol.showLocated} - |seem to be used to implement a local failure in order to negate an implicit search. - |According to the new implicit resolution rules this is no longer possible; - |the search will fail with a global ambiguity error instead. - | - |Consider using the scala.util.NotGiven class to implement similar functionality.""", - srcPos) + em"""Ambiguous implicits ${ambi.alt1.ref.symbol.showLocated} and ${ambi.alt2.ref.symbol.showLocated} + |seem to be used to implement a local failure in order to negate an implicit search. + |According to the new implicit resolution rules this is no longer possible; + |the search will fail with a global ambiguity error instead. + | + |Consider using the scala.util.NotGiven class to implement similar functionality.""", + srcPos) /** Compare the length of the baseClasses of two symbols (except for objects, * where we use the length of the companion class instead if it's bigger). diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 39d4f0868896..7dd9cf00b68a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -201,7 +201,7 @@ class Namer { typer: Typer => case tree: MemberDef => SymDenotations.canBeLocal(tree.name, flags) case _ => false if !ok then - report.error(i"modifier(s) `${flags.flagsString}` incompatible with $kind definition", tree.srcPos) + report.error(em"modifier(s) `${flags.flagsString}` incompatible with $kind definition", tree.srcPos) if adapted.is(Private) && canBeLocal then adapted | Local else adapted } @@ -762,7 +762,7 @@ class Namer { typer: Typer => } def missingType(sym: Symbol, modifier: String)(using Context): Unit = { - report.error(s"${modifier}type of implicit definition needs to be given explicitly", sym.srcPos) + report.error(em"${modifier}type of implicit definition needs to be given explicitly", sym.srcPos) sym.resetFlag(GivenOrImplicit) } @@ -831,7 +831,7 @@ class Namer { typer: Typer => for (annotTree <- original.mods.annotations) { val cls = typedAheadAnnotationClass(annotTree)(using annotCtx) if (cls eq sym) - report.error("An annotation class cannot be annotated with iself", annotTree.srcPos) + report.error(em"An annotation class cannot be annotated with iself", annotTree.srcPos) else { val ann = Annotation.deferred(cls)(typedAheadExpr(annotTree)(using annotCtx)) sym.addAnnotation(ann) @@ -1249,7 +1249,7 @@ class Namer { typer: Typer => val reason = mbrs.map(canForward(_, alias)).collect { case CanForward.No(whyNot) => i"\n$path.$name cannot be exported because it $whyNot" }.headOption.getOrElse("") - report.error(i"""no eligible member $name at $path$reason""", ctx.source.atSpan(span)) + report.error(em"""no eligible member $name at $path$reason""", ctx.source.atSpan(span)) else targets += alias @@ -1437,7 +1437,7 @@ class Namer { typer: Typer => case mt: MethodType if cls.is(Case) && mt.isParamDependent => // See issue #8073 for background report.error( - i"""Implementation restriction: case classes cannot have dependencies between parameters""", + em"""Implementation restriction: case classes cannot have dependencies between parameters""", cls.srcPos) case _ => @@ -1687,8 +1687,10 @@ class Namer { typer: Typer => if !Config.checkLevelsOnConstraints then val hygienicType = TypeOps.avoid(rhsType, termParamss.flatten) if (!hygienicType.isValueType || !(hygienicType <:< tpt.tpe)) - report.error(i"return type ${tpt.tpe} of lambda cannot be made hygienic;\n" + - i"it is not a supertype of the hygienic type $hygienicType", mdef.srcPos) + report.error( + em"""return type ${tpt.tpe} of lambda cannot be made hygienic + |it is not a supertype of the hygienic type $hygienicType""", + mdef.srcPos) //println(i"lifting $rhsType over $termParamss -> $hygienicType = ${tpt.tpe}") //println(TypeComparer.explained { implicit ctx => hygienicType <:< tpt.tpe }) case _ => diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 87a4c4517bb2..2fe5770c5b4b 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -87,7 +87,7 @@ trait QuotesAndSplices { ref(defn.QuotedRuntime_exprSplice).appliedToType(argType).appliedTo(pat) } else { - report.error(i"Type must be fully defined.\nConsider annotating the splice using a type ascription:\n ($tree: XYZ).", tree.expr.srcPos) + report.error(em"Type must be fully defined.\nConsider annotating the splice using a type ascription:\n ($tree: XYZ).", tree.expr.srcPos) tree.withType(UnspecifiedErrorType) } else { @@ -123,7 +123,7 @@ trait QuotesAndSplices { assert(ctx.mode.is(Mode.QuotedPattern)) val untpd.Apply(splice: untpd.Splice, args) = tree: @unchecked if !isFullyDefined(pt, ForceDegree.flipBottom) then - report.error(i"Type must be fully defined.", splice.srcPos) + report.error(em"Type must be fully defined.", splice.srcPos) tree.withType(UnspecifiedErrorType) else if splice.isInBraces then // ${x}(...) match an application val typedArgs = args.map(arg => typedExpr(arg)) @@ -172,10 +172,10 @@ trait QuotesAndSplices { report.error("Splice ${...} outside quotes '{...} or inline method", tree.srcPos) else if (level < 0) report.error( - s"""Splice $${...} at level $level. - | - |Inline method may contain a splice at level 0 but the contents of this splice cannot have a splice. - |""".stripMargin, tree.srcPos + em"""Splice $${...} at level $level. + | + |Inline method may contain a splice at level 0 but the contents of this splice cannot have a splice. + |""", tree.srcPos ) /** Split a typed quoted pattern is split into its type bindings, pattern expression and inner patterns. @@ -263,7 +263,7 @@ trait QuotesAndSplices { transformTypeBindingTypeDef(PatMatGivenVarName.fresh(tdef.name.toTermName), tdef, typePatBuf) else if tdef.symbol.isClass then val kind = if tdef.symbol.is(Module) then "objects" else "classes" - report.error("Implementation restriction: cannot match " + kind, tree.srcPos) + report.error(em"Implementation restriction: cannot match $kind", tree.srcPos) EmptyTree else super.transform(tree) diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 4fed08127e32..f7c7bfa2f8a1 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -58,11 +58,9 @@ object RefChecks { // constructors of different classes are allowed to have defaults if (haveDefaults.exists(x => !x.isConstructor) || owners.distinct.size < haveDefaults.size) report.error( - "in " + clazz + - ", multiple overloaded alternatives of " + haveDefaults.head + - " define default arguments" + ( - if (owners.forall(_ == clazz)) "." - else ".\nThe members with defaults are defined in " + owners.map(_.showLocated).mkString("", " and ", ".")), + em"in $clazz, multiple overloaded alternatives of ${haveDefaults.head} define default arguments${ + if owners.forall(_ == clazz) then "." + else i".\nThe members with defaults are defined in ${owners.map(_.showLocated).mkString("", " and ", ".")}"}", clazz.srcPos) } } @@ -375,7 +373,7 @@ object RefChecks { def overrideDeprecation(what: String, member: Symbol, other: Symbol, fix: String): Unit = report.deprecationWarning( - s"overriding $what${infoStringWithLocation(other)} is deprecated;\n ${infoString(member)} should be $fix.", + em"overriding $what${infoStringWithLocation(other)} is deprecated;\n ${infoString(member)} should be $fix.", if member.owner == clazz then member.srcPos else clazz.srcPos) def autoOverride(sym: Symbol) = @@ -461,7 +459,7 @@ object RefChecks { if (autoOverride(member) || other.owner.isAllOf(JavaInterface) && warnOnMigration( - "`override` modifier required when a Java 8 default method is re-implemented".toMessage, + em"`override` modifier required when a Java 8 default method is re-implemented", member.srcPos, version = `3.0`)) member.setFlag(Override) else if (member.isType && self.memberInfo(member) =:= self.memberInfo(other)) @@ -518,7 +516,7 @@ object RefChecks { overrideError(i"cannot override val parameter ${other.showLocated}") else report.deprecationWarning( - i"overriding val parameter ${other.showLocated} is deprecated, will be illegal in a future version", + em"overriding val parameter ${other.showLocated} is deprecated, will be illegal in a future version", member.srcPos) else if !other.isExperimental && member.hasAnnotation(defn.ExperimentalAnnot) then // (1.12) overrideError("may not override non-experimental member") @@ -541,7 +539,7 @@ object RefChecks { val abstractErrors = new mutable.ListBuffer[String] def abstractErrorMessage = // a little formatting polish - if (abstractErrors.size <= 2) abstractErrors mkString " " + if (abstractErrors.size <= 2) abstractErrors.mkString(" ") else abstractErrors.tail.mkString(abstractErrors.head + ":\n", "\n", "") def abstractClassError(mustBeMixin: Boolean, msg: String): Unit = { @@ -943,7 +941,7 @@ object RefChecks { for bc <- cls.baseClasses.tail do val other = sym.matchingDecl(bc, cls.thisType) if other.exists then - report.error(i"private $sym cannot override ${other.showLocated}", sym.srcPos) + report.error(em"private $sym cannot override ${other.showLocated}", sym.srcPos) end checkNoPrivateOverrides /** Check that unary method definition do not receive parameters. diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index e3f5382ecad7..94862e282b69 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -476,8 +476,8 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): val elemLabels = cls.children.map(c => ConstantType(Constant(c.name.toString))) def internalError(msg: => String)(using Context): Unit = - report.error(i"""Internal error when synthesizing sum mirror for $cls: - |$msg""".stripMargin, ctx.source.atSpan(span)) + report.error(em"""Internal error when synthesizing sum mirror for $cls: + |$msg""", ctx.source.atSpan(span)) def childPrefix(child: Symbol)(using Context): Type = val symPre = TypeOps.childPrefix(pre, cls, child) @@ -691,10 +691,11 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): val manifest = synthesize(fullyDefinedType(arg, "Manifest argument", ctx.source.atSpan(span)), kind, topLevel = true) if manifest != EmptyTree then report.deprecationWarning( - i"""Compiler synthesis of Manifest and OptManifest is deprecated, instead - |replace with the type `scala.reflect.ClassTag[$arg]`. - |Alternatively, consider using the new metaprogramming features of Scala 3, - |see https://docs.scala-lang.org/scala3/reference/metaprogramming.html""", ctx.source.atSpan(span)) + em"""Compiler synthesis of Manifest and OptManifest is deprecated, instead + |replace with the type `scala.reflect.ClassTag[$arg]`. + |Alternatively, consider using the new metaprogramming features of Scala 3, + |see https://docs.scala-lang.org/scala3/reference/metaprogramming.html""", + ctx.source.atSpan(span)) withNoErrors(manifest) case _ => EmptyTreeNoError diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index b265c6343651..68bdd4f4970a 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -31,8 +31,9 @@ trait TypeAssigner { c case _ => report.error( - if (qual.isEmpty) tree.show + " can be used only in a class, object, or template" - else qual.show + " is not an enclosing class", tree.srcPos) + if qual.isEmpty then em"$tree can be used only in a class, object, or template" + else em"$qual is not an enclosing class", + tree.srcPos) NoSymbol } } @@ -127,7 +128,7 @@ trait TypeAssigner { def arrayElemType = qual1.tpe.widen match case JavaArrayType(elemtp) => elemtp case qualType => - report.error("Expected Array but was " + qualType.show, tree.srcPos) + report.error(em"Expected Array but was $qualType", tree.srcPos) defn.NothingType val name = tree.name diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 3f1e4b175338..727433bdb21c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1212,8 +1212,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer && defn.isContextFunctionType(pt1.nonPrivateMember(nme.apply).info.finalResultType) then report.error( - i"""Implementation restriction: Expected result type $pt1 - |is a curried dependent context function type. Such types are not yet supported.""", + em"""Implementation restriction: Expected result type $pt1 + |is a curried dependent context function type. Such types are not yet supported.""", pos) pt1 match { case tp: TypeParamRef => @@ -1324,7 +1324,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val appDef = typed(appDef0).asInstanceOf[DefDef] val mt = appDef.symbol.info.asInstanceOf[MethodType] if (mt.isParamDependent) - report.error(i"$mt is an illegal function type because it has inter-parameter dependencies", tree.srcPos) + report.error(em"$mt is an illegal function type because it has inter-parameter dependencies", tree.srcPos) val resTpt = TypeTree(mt.nonDependentResultApprox).withSpan(body.span) val typeArgs = appDef.termParamss.head.map(_.tpt) :+ resTpt val tycon = TypeTree(funSym.typeRef) @@ -2446,7 +2446,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // error if the same parent was explicitly added in user code. if !tree.span.isSourceDerived then return EmptyTree - if !ctx.isAfterTyper then report.error(i"$psym is extended twice", tree.srcPos) + if !ctx.isAfterTyper then report.error(em"$psym is extended twice", tree.srcPos) else seenParents += psym val result = ensureConstrCall(cls, parent, psym)(using superCtx) @@ -2563,7 +2563,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer && effectiveOwner.is(Trait) && !effectiveOwner.derivesFrom(defn.ObjectClass) then - report.error(i"$cls cannot be defined in universal $effectiveOwner", cdef.srcPos) + report.error(em"$cls cannot be defined in universal $effectiveOwner", cdef.srcPos) // Temporarily set the typed class def as root tree so that we have at least some // information in the IDE in case we never reach `SetRootTree`. @@ -2750,8 +2750,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if ((prefix ++ suffix).isEmpty) "simply leave out the trailing ` _`" else s"use `$prefix$suffix` instead" report.errorOrMigrationWarning( - i"""The syntax ` _` is no longer supported; - |you can $remedy""", + em"""The syntax ` _` is no longer supported; + |you can $remedy""", tree.srcPos, from = future) if sourceVersion.isMigrating then @@ -2883,7 +2883,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else "" val namePos = tree.sourcePos.withSpan(tree.nameSpan) report.errorOrMigrationWarning( - s"`?` is not a valid type name$addendum", namePos, from = `3.0`) + em"`?` is not a valid type name$addendum", namePos, from = `3.0`) if tree.isClassDef then typedClassDef(tree, sym.asClass)(using ctx.localContext(tree, sym)) else @@ -3796,8 +3796,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer } else { report.error( - """Scala 2 macro cannot be used in Dotty. See https://docs.scala-lang.org/scala3/reference/dropped-features/macros.html - |To turn this error into a warning, pass -Xignore-scala2-macros to the compiler""".stripMargin, tree.srcPos.startPos) + em"""Scala 2 macro cannot be used in Dotty. See https://docs.scala-lang.org/scala3/reference/dropped-features/macros.html + |To turn this error into a warning, pass -Xignore-scala2-macros to the compiler""", + tree.srcPos.startPos) tree } else TypeComparer.testSubType(tree.tpe.widenExpr, pt) match @@ -4228,11 +4229,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case _ => if !config.Feature.scala2ExperimentalMacroEnabled then report.error( - """Scala 2 macro definition needs to be enabled - |by making the implicit value scala.language.experimental.macros visible. - |This can be achieved by adding the import clause 'import scala.language.experimental.macros' - |or by setting the compiler option -language:experimental.macros. - """.stripMargin, call.srcPos) + em"""Scala 2 macro definition needs to be enabled + |by making the implicit value scala.language.experimental.macros visible. + |This can be achieved by adding the import clause 'import scala.language.experimental.macros' + |or by setting the compiler option -language:experimental.macros. + """, + call.srcPos) call match case call: untpd.Ident => typedIdent(call, defn.AnyType) @@ -4247,7 +4249,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer typedTypeApply(call2, defn.AnyType) } case _ => - report.error("Invalid Scala 2 macro " + call.show, call.srcPos) + report.error(em"Invalid Scala 2 macro $call", call.srcPos) EmptyTree else typedExpr(call, defn.AnyType) diff --git a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala index 53646558cf5c..bcfc9288d862 100644 --- a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala +++ b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala @@ -164,11 +164,11 @@ class VarianceChecker(using Context) { i"\n${hl("enum case")} ${towner.name} requires explicit declaration of $tvar to resolve this issue.\n$example" else "" - i"${varianceLabel(tvar.flags)} $tvar occurs in ${varianceLabel(required)} position in type ${sym.info} of $sym$enumAddendum" + em"${varianceLabel(tvar.flags)} $tvar occurs in ${varianceLabel(required)} position in type ${sym.info} of $sym$enumAddendum" if (migrateTo3 && (sym.owner.isConstructor || sym.ownersIterator.exists(_.isAllOf(ProtectedLocal)))) report.migrationWarning( - s"According to new variance rules, this is no longer accepted; need to annotate with @uncheckedVariance:\n$msg", + msg.prepend("According to new variance rules, this is no longer accepted; need to annotate with @uncheckedVariance\n"), pos) // patch(Span(pos.end), " @scala.annotation.unchecked.uncheckedVariance") // Patch is disabled until two TODOs are solved: From 40d29d65dc86ea64fd8a2add1b3f8b67c5c77b56 Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 24 Nov 2022 12:52:12 +0100 Subject: [PATCH 13/14] Polishings --- compiler/src/dotty/tools/dotc/core/Decorators.scala | 2 +- compiler/src/dotty/tools/dotc/reporting/Message.scala | 4 ++-- compiler/src/dotty/tools/package.scala | 11 ++++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Decorators.scala b/compiler/src/dotty/tools/dotc/core/Decorators.scala index abbdd97daf36..4724b8cd8ac3 100644 --- a/compiler/src/dotty/tools/dotc/core/Decorators.scala +++ b/compiler/src/dotty/tools/dotc/core/Decorators.scala @@ -11,7 +11,7 @@ import printing.{ Printer, Showable }, printing.Formatting._, printing.Texts._ import transform.MegaPhase import reporting.{Message, NoExplanation} -/** This object provides useful implicit decorators for types defined elsewhere */ +/** This object provides useful extension methods for types defined elsewhere */ object Decorators { /** Extension methods for toType/TermName methods on PreNames. diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index 9e8d7289801b..e5fba28ee183 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -15,12 +15,12 @@ import scala.annotation.threadUnsafe /** ## Tips for error message generation * - * - You can use the `em` interpolator for error messages. It's defined in decorators. + * - You can use the `em` interpolator for error messages. It's defined in core.Decorators. * - You can also use a simple string argument for `error` or `warning` (not for the other variants), * but the string should not be interpolated or composed of objects that require a * Context for evaluation. * - When embedding interpolated substrings defined elsewhere in error messages, - * make sure they are defined as def's instead of vals. That way, the + * use `i` and make sure they are defined as def's instead of vals. That way, the * possibly expensive interpolation will performed only in the case where the message * is eventually printed. Note: At least during typer, it's common for messages * to be discarded without being printed. Also, by making them defs, you ensure that diff --git a/compiler/src/dotty/tools/package.scala b/compiler/src/dotty/tools/package.scala index 4ec9ae40c421..f90355b1fa8e 100644 --- a/compiler/src/dotty/tools/package.scala +++ b/compiler/src/dotty/tools/package.scala @@ -1,10 +1,6 @@ package dotty package object tools { - // Ensure this object is already classloaded, since it's only actually used - // when handling stack overflows and every operation (including class loading) - // risks failing. - dotty.tools.dotc.core.handleRecursive val ListOfNil: List[Nil.type] = Nil :: Nil @@ -49,4 +45,9 @@ package object tools { val e = if msg == null then AssertionError() else AssertionError("assertion failed: " + msg) e.setStackTrace(Array()) throw e -} + + // Ensure this object is already classloaded, since it's only actually used + // when handling stack overflows and every operation (including class loading) + // risks failing. + dotty.tools.dotc.core.handleRecursive + } From d704ede5fe08a6ee335ef2be4921ce2faa55d0e7 Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 24 Nov 2022 14:01:14 +0100 Subject: [PATCH 14/14] Refactor Message.Seen class - Use aggregation instead of inheritance - Encapsulate explanations inside Seen --- .../dotty/tools/dotc/reporting/Message.scala | 187 ++++++++++-------- 1 file changed, 100 insertions(+), 87 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index e5fba28ee183..a1fe6773c1d2 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -27,7 +27,7 @@ import scala.annotation.threadUnsafe * they will be evaluated in the Message context, which makes formatting safer * and more robust. * - For common messages, or messages that might require explanation, prefer defining - * a new Message class in messages and use that instead. The advantage is that these + * a new `Message` class in file `messages.scala` and use that instead. The advantage is that these * messages have unique IDs that can be referenced elsewhere. */ object Message: @@ -45,18 +45,33 @@ object Message: private case class SeenKey(str: String, isType: Boolean) - private class Seen(disambiguate: Boolean) extends collection.mutable.HashMap[SeenKey, List[Recorded]]: - override def default(key: SeenKey) = Nil + /** A class that records printed items of one of the types in `Recorded`, + * adds superscripts for disambiguations, and can explain recorded symbols + * in ` where` clause + */ + private class Seen(disambiguate: Boolean): + + val seen = new collection.mutable.HashMap[SeenKey, List[Recorded]]: + override def default(key: SeenKey) = Nil var nonSensical = false + + /** If false, stop all recordings */ private var recordOK = disambiguate /** Clear all entries and stop further entries to be added */ def disable() = - clear() + seen.clear() recordOK = false - def record(str: String, isType: Boolean, entry: Recorded)(using Context): String = { + /** Record an entry `entry` with given String representation `str` and a + * type/term namespace identified by `isType`. + * If the entry was not yet recorded, allocate the next superscript corresponding + * to the same string in the same name space. The first recording is the string proper + * and following recordings get consecutive superscripts starting with 2. + * @return The possibly superscripted version of `str`. + */ + def record(str: String, isType: Boolean, entry: Recorded)(using Context): String = if !recordOK then return str //println(s"recording $str, $isType, $entry") @@ -71,15 +86,15 @@ object Message: case _ => e1 } val key = SeenKey(str, isType) - val existing = apply(key) + val existing = seen(key) lazy val dealiased = followAlias(entry) // alts: The alternatives in `existing` that are equal, or follow (an alias of) `entry` var alts = existing.dropWhile(alt => dealiased ne followAlias(alt)) - if (alts.isEmpty) { + if alts.isEmpty then alts = entry :: existing - update(key, alts) - } + seen(key) = alts + val suffix = alts.length match { case 1 => "" case n => n.toString.toCharArray.map { @@ -96,88 +111,86 @@ object Message: }.mkString } str + suffix - } - end Seen + end record + + /** Create explanation for single `Recorded` type or symbol */ + private def explanation(entry: AnyRef)(using Context): String = + def boundStr(bound: Type, default: ClassSymbol, cmp: String) = + if (bound.isRef(default)) "" else i"$cmp $bound" + + def boundsStr(bounds: TypeBounds): String = { + val lo = boundStr(bounds.lo, defn.NothingClass, ">:") + val hi = boundStr(bounds.hi, defn.AnyClass, "<:") + if (lo.isEmpty) hi + else if (hi.isEmpty) lo + else s"$lo and $hi" + } - /** Create explanation for single `Recorded` type or symbol */ - def explanation(entry: AnyRef)(using Context): String = - def boundStr(bound: Type, default: ClassSymbol, cmp: String) = - if (bound.isRef(default)) "" else i"$cmp $bound" - - def boundsStr(bounds: TypeBounds): String = { - val lo = boundStr(bounds.lo, defn.NothingClass, ">:") - val hi = boundStr(bounds.hi, defn.AnyClass, "<:") - if (lo.isEmpty) hi - else if (hi.isEmpty) lo - else s"$lo and $hi" - } - - def addendum(cat: String, info: Type): String = info match { - case bounds @ TypeBounds(lo, hi) if bounds ne TypeBounds.empty => - if (lo eq hi) i" which is an alias of $lo" - else i" with $cat ${boundsStr(bounds)}" - case _ => - "" - } - - entry match { - case param: TypeParamRef => - s"is a type variable${addendum("constraint", TypeComparer.bounds(param))}" - case param: TermParamRef => - s"is a reference to a value parameter" - case sym: Symbol => - val info = - if (ctx.gadt.contains(sym)) - sym.info & ctx.gadt.fullBounds(sym) - else - sym.info - s"is a ${ctx.printer.kindString(sym)}${sym.showExtendedLocation}${addendum("bounds", info)}" - case tp: SkolemType => - s"is an unknown value of type ${tp.widen.show}" - } - end explanation - - /** Turns a `Seen` into a `String` to produce an explanation for types on the - * form `where: T is...` - * - * @return string disambiguating types - */ - private def explanations(seen: Seen)(using Context): String = - def needsExplanation(entry: Recorded) = entry match { - case param: TypeParamRef => ctx.typerState.constraint.contains(param) - case param: ParamRef => false - case skolem: SkolemType => true - case sym: Symbol => - ctx.gadt.contains(sym) && ctx.gadt.fullBounds(sym) != TypeBounds.empty - } - - val toExplain: List[(String, Recorded)] = seen.toList.flatMap { kvs => - val res: List[(String, Recorded)] = kvs match { - case (key, entry :: Nil) => - if (needsExplanation(entry)) (key.str, entry) :: Nil else Nil - case (key, entries) => - for (alt <- entries) yield { - val tickedString = seen.record(key.str, key.isType, alt) - (tickedString, alt) - } + def addendum(cat: String, info: Type): String = info match { + case bounds @ TypeBounds(lo, hi) if bounds ne TypeBounds.empty => + if (lo eq hi) i" which is an alias of $lo" + else i" with $cat ${boundsStr(bounds)}" + case _ => + "" + } + + entry match { + case param: TypeParamRef => + s"is a type variable${addendum("constraint", TypeComparer.bounds(param))}" + case param: TermParamRef => + s"is a reference to a value parameter" + case sym: Symbol => + val info = + if (ctx.gadt.contains(sym)) + sym.info & ctx.gadt.fullBounds(sym) + else + sym.info + s"is a ${ctx.printer.kindString(sym)}${sym.showExtendedLocation}${addendum("bounds", info)}" + case tp: SkolemType => + s"is an unknown value of type ${tp.widen.show}" + } + end explanation + + /** Produce a where clause with explanations for recorded iterms. + */ + def explanations(using Context): String = + def needsExplanation(entry: Recorded) = entry match { + case param: TypeParamRef => ctx.typerState.constraint.contains(param) + case param: ParamRef => false + case skolem: SkolemType => true + case sym: Symbol => + ctx.gadt.contains(sym) && ctx.gadt.fullBounds(sym) != TypeBounds.empty } - res // help the inferrencer out - }.sortBy(_._1) - - def columnar(parts: List[(String, String)]): List[String] = { - lazy val maxLen = parts.map(_._1.length).max - parts.map { - case (leader, trailer) => - val variable = hl(leader) - s"""$variable${" " * (maxLen - leader.length)} $trailer""" + + val toExplain: List[(String, Recorded)] = seen.toList.flatMap { kvs => + val res: List[(String, Recorded)] = kvs match { + case (key, entry :: Nil) => + if (needsExplanation(entry)) (key.str, entry) :: Nil else Nil + case (key, entries) => + for (alt <- entries) yield { + val tickedString = record(key.str, key.isType, alt) + (tickedString, alt) + } + } + res // help the inferrencer out + }.sortBy(_._1) + + def columnar(parts: List[(String, String)]): List[String] = { + lazy val maxLen = parts.map(_._1.length).max + parts.map { + case (leader, trailer) => + val variable = hl(leader) + s"""$variable${" " * (maxLen - leader.length)} $trailer""" + } } - } - val explainParts = toExplain.map { case (str, entry) => (str, explanation(entry)) } - val explainLines = columnar(explainParts) - if (explainLines.isEmpty) "" else i"where: $explainLines%\n %\n" - end explanations + val explainParts = toExplain.map { case (str, entry) => (str, explanation(entry)) } + val explainLines = columnar(explainParts) + if (explainLines.isEmpty) "" else i"where: $explainLines%\n %\n" + end explanations + end Seen + /** Printer to be used when formatting messages */ private class Printer(val seen: Seen, _ctx: Context) extends RefinedPrinter(_ctx): /** True if printer should a show source module instead of its module class */ @@ -284,7 +297,7 @@ abstract class Message(val errorId: ErrorMessageID)(using Context) { self => else ctx.printer match case msgPrinter: Message.Printer => myIsNonSensical = msgPrinter.seen.nonSensical - val addendum = explanations(msgPrinter.seen) + val addendum = msgPrinter.seen.explanations msgPrinter.seen.disable() // Clear entries and stop futher recording so that messages containing the current // one don't repeat the explanations or use explanations from the msgPostscript.