From 1a0908cbf3f508172a60b8b72476854b5cc8b8ce Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 19 Oct 2022 13:35:22 +0200 Subject: [PATCH] Drop `toNoExplanation` implicit conversion `toNoExplanation` was an old-style implicit conversion. It could not be made a `Conversion` since it was defined over a call-by-name argument. It also hid important implementation details since it delayed constructing the argument. So knowing whether a string would be evaluated strictly or lazily depended on knowing how type inference would work. We replace the conversion by a combination of - an extension method `.toMessage` converting a lazy string explicitly, and - some overloading of core methods that take either a message or a lazy string. We should over time get rid of overloads and convert most strings passed as messges to proper messages. But that will take time. At least by now we see better where the work is needed. --- .../dotty/tools/dotc/core/Decorators.scala | 4 ++ .../dotty/tools/dotc/core/TypeErrors.scala | 4 +- .../src/dotty/tools/dotc/core/Types.scala | 6 ++- .../core/unpickleScala2/Scala2Unpickler.scala | 3 +- .../dotty/tools/dotc/inlines/Inlines.scala | 2 +- .../dotty/tools/dotc/parsing/Parsers.scala | 45 +++++++++------- .../dotty/tools/dotc/parsing/Scanners.scala | 2 +- .../dotc/parsing/xml/MarkupParsers.scala | 3 +- compiler/src/dotty/tools/dotc/report.scala | 51 ++++++++++++++----- .../tools/dotc/reporting/Diagnostic.scala | 7 ++- .../dotty/tools/dotc/reporting/Message.scala | 6 --- .../dotty/tools/dotc/reporting/Reporter.scala | 3 +- .../dotty/tools/dotc/typer/Applications.scala | 8 +-- .../src/dotty/tools/dotc/typer/Checking.scala | 15 +++--- .../tools/dotc/typer/ErrorReporting.scala | 6 +++ .../dotty/tools/dotc/typer/Implicits.scala | 2 +- .../dotty/tools/dotc/typer/RefChecks.scala | 2 +- .../dotty/tools/dotc/typer/TypeAssigner.scala | 2 +- .../src/dotty/tools/dotc/typer/Typer.scala | 4 +- .../tools/dotc/StringFormatterTest.scala | 2 +- .../dotc/config/ScalaSettingsTests.scala | 5 +- 21 files changed, 117 insertions(+), 65 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Decorators.scala b/compiler/src/dotty/tools/dotc/core/Decorators.scala index 59440d1cb965..d392a4e3079a 100644 --- a/compiler/src/dotty/tools/dotc/core/Decorators.scala +++ b/compiler/src/dotty/tools/dotc/core/Decorators.scala @@ -9,6 +9,7 @@ import scala.util.control.NonFatal import Contexts._, Names._, Phases._, Symbols._ 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 */ object Decorators { @@ -57,6 +58,9 @@ object Decorators { padding + s.replace("\n", "\n" + padding) end extension + extension (str: => String) + def toMessage: Message = reporting.NoExplanation(str) + /** 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 5816e1254873..a3b594eb0f09 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala @@ -18,13 +18,14 @@ 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 + 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 { @@ -38,6 +39,7 @@ class MissingType(pre: Type, name: Name) extends TypeError { 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 } } diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 605310989384..dc1f02c8a0fd 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -5296,16 +5296,18 @@ 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: 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" + case None => "error message from previous run no longer available".toMessage object UnspecifiedErrorType extends ErrorType { - override def msg(using Context): Message = "unspecified error" + override def msg(using Context): Message = "unspecified error".toMessage } /* 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/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 333cd9fa9ec3..b5f95bb8b19c 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -20,6 +20,7 @@ import printing.Texts._ import printing.Printer import io.AbstractFile import util.common._ +import util.NoSourcePosition import typer.Checking.checkNonCyclic import typer.Nullables._ import transform.SymUtils._ @@ -744,7 +745,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas val anyTypes = boundSyms map (_ => defn.AnyType) val boundBounds = boundSyms map (_.info.bounds.hi) val tp2 = tp1.subst(boundSyms, boundBounds).subst(boundSyms, anyTypes) - report.warning(FailureToEliminateExistential(tp, tp1, tp2, boundSyms, classRoot.symbol)) + report.warning(FailureToEliminateExistential(tp, tp1, tp2, boundSyms, classRoot.symbol), NoSourcePosition) tp2 } else tp1 diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index d1a88406fe45..8be23b932e98 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -155,7 +155,7 @@ object Inlines: 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.""", + |You can use ${setting.name} to change the limit.""".toMessage, (tree :: enclosingInlineds).last.srcPos ) if ctx.base.stopInlining && enclosingInlineds.isEmpty then diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 309dd8a20aba..ab8f578b9ac4 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -142,7 +142,12 @@ object Parsers { val length = if offset == in.offset && in.name != null then in.name.show.length else 0 syntaxError(msg, Span(offset, offset + length)) lastErrorOffset = in.offset - end if + + 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. @@ -150,6 +155,9 @@ object Parsers { 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(Select(rootDot(nme.scala), nme.Predef), nme.???) } @@ -259,9 +267,6 @@ object Parsers { in.skip() lastErrorOffset = in.offset - def warning(msg: Message, sourcePos: SourcePosition): Unit = - report.warning(msg, sourcePos) - def warning(msg: Message, offset: Int = in.offset): Unit = report.warning(msg, source.atSpan(Span(offset))) @@ -283,6 +288,9 @@ 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) @@ -350,7 +358,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") + else i"end of $what expected but ${showToken(found)} found".toMessage) if mustStartStatTokens.contains(found) then false // it's a statement that might be legal in an outer context else @@ -610,11 +618,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: + i"""Line is indented too far to the right, or a `{` is missing before: | - |${t.tryToShow}""" + |${t.tryToShow}""".toMessage else - in.spaceTabMismatchMsg(startIndentWidth, nextIndentWidth), + in.spaceTabMismatchMsg(startIndentWidth, nextIndentWidth).toMessage, in.next.offset ) t @@ -627,7 +635,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", in.next.offset) + warning(i"Line is indented too far to the right, or a `{` or `:` is missing".toMessage, in.next.offset) /* -------- REWRITES ----------------------------------------------------------- */ @@ -1732,7 +1740,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") + deprecationWarning(em"`_` is deprecated for wildcard arguments of types: use `?` instead".toMessage) patch(source, Span(in.offset, in.offset + 1), "?") val start = in.skipToken() typeBounds().withSpan(Span(start, in.lastOffset, start)) @@ -2171,10 +2179,11 @@ object Parsers { else Literal(Constant(())) // finally without an expression } else { - if (handler.isEmpty) warning( - EmptyCatchAndFinallyBlock(body), - source.atSpan(Span(tryOffset, endOffset(body))) - ) + if handler.isEmpty then + report.warning( + EmptyCatchAndFinallyBlock(body), + source.atSpan(Span(tryOffset, endOffset(body))) + ) EmptyTree } ParsedTry(body, handler, finalizer) @@ -2768,7 +2777,7 @@ object Parsers { 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.""") + |an indented case.""".toMessage) expr() else block() }) @@ -2989,7 +2998,8 @@ object Parsers { inBrackets { if in.token == THIS then if sourceVersion.isAtLeast(future) then - deprecationWarning("The [this] qualifier will be deprecated in the future; it should be dropped.") + deprecationWarning( + "The [this] qualifier will be deprecated in the future; it should be dropped.".toMessage) in.nextToken() mods | Local else mods.withPrivateWithin(ident().toTypeName) @@ -3471,7 +3481,8 @@ object Parsers { if sourceVersion.isAtLeast(future) then deprecationWarning( em"""`= _` has been deprecated; use `= uninitialized` instead. - |`uninitialized` can be imported with `scala.compiletime.uninitialized`.""", rhsOffset) + |`uninitialized` can be imported with `scala.compiletime.uninitialized`.""".toMessage, + rhsOffset) placeholderParams = placeholderParams.tail atSpan(rhs0.span) { Ident(nme.WILDCARD) } case rhs0 => rhs0 diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 082112d800d9..a4eff045b4ac 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -112,7 +112,7 @@ object Scanners { /** signal an error where the input ended in the middle of a token */ def incompleteInputError(msg: String): Unit = { - report.incompleteInputError(msg, sourcePos()) + report.incompleteInputError(msg.toMessage, sourcePos()) token = EOF errOffset = offset } diff --git a/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala b/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala index 591042961dbb..3d9f5fb7ad6d 100644 --- a/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala @@ -13,6 +13,7 @@ import Parsers._ import util.Spans._ import core._ import Constants._ +import Decorators.toMessage import util.SourceFile import Utility._ @@ -379,7 +380,7 @@ object MarkupParsers { ts(0) } }, - msg => parser.incompleteInputError(msg) + msg => parser.incompleteInputError(msg.toMessage) ) /** @see xmlPattern. resynchronizes after successful parse diff --git a/compiler/src/dotty/tools/dotc/report.scala b/compiler/src/dotty/tools/dotc/report.scala index 5addb11f1a3c..00399ecbfd0a 100644 --- a/compiler/src/dotty/tools/dotc/report.scala +++ b/compiler/src/dotty/tools/dotc/report.scala @@ -18,23 +18,35 @@ object report: if ctx.settings.verbose.value then echo(msg, pos) def echo(msg: => String, pos: SrcPos = NoSourcePosition)(using Context): Unit = - ctx.reporter.report(new Info(msg, pos.sourcePos)) + ctx.reporter.report(new Info(msg.toMessage, pos.sourcePos)) private def issueWarning(warning: Warning)(using Context): Unit = ctx.reporter.report(warning) - def deprecationWarning(msg: Message, pos: SrcPos = NoSourcePosition)(using Context): Unit = + def deprecationWarning(msg: Message, pos: SrcPos)(using Context): Unit = issueWarning(new DeprecationWarning(msg, pos.sourcePos)) - def migrationWarning(msg: Message, pos: SrcPos = NoSourcePosition)(using Context): Unit = + 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 uncheckedWarning(msg: Message, pos: SrcPos = NoSourcePosition)(using Context): Unit = + 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 featureWarning(msg: Message, pos: SrcPos = NoSourcePosition)(using Context): Unit = + 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" @@ -52,30 +64,43 @@ object report: |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, pos.sourcePos)) + else issueWarning(new FeatureWarning(msg.toMessage, pos.sourcePos)) } - def warning(msg: Message, pos: SrcPos = NoSourcePosition)(using Context): Unit = + def warning(msg: Message, pos: SrcPos)(using Context): Unit = issueWarning(new Warning(msg, addInlineds(pos))) - def error(msg: Message, pos: SrcPos = NoSourcePosition, sticky: Boolean = false)(using Context): Unit = + def warning(msg: => String, pos: SrcPos = NoSourcePosition)(using Context): Unit = + warning(msg.toMessage, pos) + + def error(msg: Message, pos: SrcPos)(using Context): Unit = val fullPos = addInlineds(pos) - ctx.reporter.report(if (sticky) new StickyError(msg, fullPos) else new Error(msg, fullPos)) + ctx.reporter.report(new Error(msg, fullPos)) if ctx.settings.YdebugError.value then Thread.dumpStack() + def error(msg: => String, pos: SrcPos = NoSourcePosition)(using Context): Unit = + error(msg.toMessage, pos) + def error(ex: TypeError, pos: SrcPos)(using Context): Unit = - error(ex.toMessage, pos, sticky = true) - if ctx.settings.YdebugTypeError.value then ex.printStackTrace() + val fullPos = addInlineds(pos) + ctx.reporter.report(new StickyError(ex.toMessage, fullPos)) + if ctx.settings.YdebugError.value then Thread.dumpStack() - def errorOrMigrationWarning(msg: Message, pos: SrcPos = NoSourcePosition, from: SourceVersion)(using Context): Unit = + def errorOrMigrationWarning(msg: Message, pos: SrcPos, from: SourceVersion)(using Context): Unit = if sourceVersion.isAtLeast(from) then if sourceVersion.isMigrating && sourceVersion.ordinal <= from.ordinal then migrationWarning(msg, pos) else error(msg, pos) - def gradualErrorOrMigrationWarning(msg: Message, pos: SrcPos = NoSourcePosition, warnFrom: SourceVersion, errorFrom: SourceVersion)(using Context): Unit = + 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/Diagnostic.scala b/compiler/src/dotty/tools/dotc/reporting/Diagnostic.scala index dec13a4f5925..a92da7821fab 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Diagnostic.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Diagnostic.scala @@ -11,6 +11,7 @@ import dotty.tools.dotc.util.SourcePosition import java.util.Optional import scala.util.chaining._ +import core.Decorators.toMessage object Diagnostic: @@ -23,7 +24,8 @@ object Diagnostic: class Error( msg: Message, pos: SourcePosition - ) extends Diagnostic(msg, pos, ERROR) + ) extends Diagnostic(msg, pos, ERROR): + def this(str: => String, pos: SourcePosition) = this(str.toMessage, pos) /** A sticky error is an error that should not be hidden by backtracking and * trying some alternative path. Typically, errors issued after catching @@ -46,7 +48,8 @@ object Diagnostic: class Info( msg: Message, pos: SourcePosition - ) extends Diagnostic(msg, pos, INFO) + ) extends Diagnostic(msg, pos, INFO): + def this(str: => String, pos: SourcePosition) = this(str.toMessage, pos) abstract class ConditionalWarning( msg: Message, diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index 77e1336a990c..9e397d606491 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -13,12 +13,6 @@ object Message { val nonSensicalStartTag: String = "" val nonSensicalEndTag: String = "" - /** This implicit conversion provides a fallback for error messages that have - * not yet been ported to the new scheme. Comment out this `implicit def` to - * see where old errors still exist - */ - implicit def toNoExplanation(str: => String): Message = NoExplanation(str) - 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 "" diff --git a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala index 0d5acaef4960..497e77ae4a7c 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala @@ -14,6 +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 object Reporter { /** Convert a SimpleReporter into a real Reporter */ @@ -218,7 +219,7 @@ abstract class Reporter extends interfaces.ReporterResult { 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, NoSourcePosition)) + report(Warning(msg.toMessage, NoSourcePosition)) /** Print the summary of warnings and errors */ def printSummary()(using Context): Unit = { diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index d38ed1676590..15493e6805dc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -479,7 +479,7 @@ trait Applications extends Compatibility { matchArgs(orderedArgs, methType.paramInfos, 0) case _ => if (methType.isError) ok = false - else fail(s"$methString does not take parameters") + else fail(s"$methString does not take parameters".toMessage) } /** The application was successful */ @@ -522,7 +522,7 @@ trait Applications extends Compatibility { s"parameter $aname of $methString is already instantiated" else s"$methString does not have a parameter $aname" - fail(msg, arg.asInstanceOf[Arg]) + fail(msg.toMessage, arg.asInstanceOf[Arg]) arg :: handleNamed(pnamesRest, args1, nameToArg, toDrop) } case arg :: args1 => @@ -564,7 +564,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", arg) + fail(em"Sequence argument type annotation `*` cannot be used here:\n$addendum".toMessage, arg) /** Add result of typing argument `arg` against parameter type `formal`. * @return The remaining formal parameter types. If the method is parameter-dependent @@ -651,7 +651,7 @@ trait Applications extends Compatibility { i"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, arg) + fail(msg.toMessage, arg) case nil => } } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 27d02f4cc0bf..c53213d7bd37 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -472,9 +472,10 @@ object Checking { if (sym.isOneOf(flag)) fail(AbstractMemberMayNotHaveModifier(sym, flag)) def checkNoConflict(flag1: FlagSet, flag2: FlagSet, msg: => String) = - if (sym.isAllOf(flag1 | flag2)) fail(msg) + if (sym.isAllOf(flag1 | flag2)) fail(msg.toMessage) 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") + if sym.isAllOf(flag1 | flag2) then + fail(i"illegal combination of modifiers: `${flag1.flagsString}` and `${flag2.flagsString}` for: $sym".toMessage) def checkApplicable(flag: Flag, ok: Boolean) = if sym.is(flag, butNot = Synthetic) && !ok then fail(ModifierNotAllowedForDefinition(flag)) @@ -494,15 +495,15 @@ object Checking { } if sym.is(Transparent) then if sym.isType then - if !sym.is(Trait) then fail(em"`transparent` can only be used for traits") + if !sym.is(Trait) then fail(em"`transparent` can only be used for traits".toMessage) else - if !sym.isInlineMethod then fail(em"`transparent` can only be used for inline methods") + if !sym.isInlineMethod then fail(em"`transparent` can only be used for inline methods".toMessage) 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}") + fail(em"only classes can be ${(sym.flags & ClassOnlyFlags).flagsString}".toMessage) if (sym.is(AbsOverride) && !sym.owner.is(Trait)) fail(AbstractOverrideOnlyInTraits(sym)) if sym.is(Trait) then @@ -519,7 +520,7 @@ object Checking { if !sym.isOneOf(Method | ModuleVal) then fail(TailrecNotApplicable(sym)) else if sym.is(Inline) then - fail("Inline methods cannot be @tailrec") + fail("Inline methods cannot be @tailrec".toMessage) if sym.hasAnnotation(defn.TargetNameAnnot) && sym.isClass && sym.isTopLevelClass then fail(TargetNameOnTopLevelClass(sym)) if (sym.hasAnnotation(defn.NativeAnnot)) { @@ -538,7 +539,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("Traits cannot have secondary constructors" + addendum) + fail(s"Traits cannot have secondary constructors$addendum".toMessage) checkApplicable(Inline, sym.isTerm && !sym.isOneOf(Mutable | Module)) checkApplicable(Lazy, !sym.isOneOf(Method | Mutable)) if (sym.isType && !sym.isOneOf(Deferred | JavaDefined)) diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index a8920274b40a..3034253adb61 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -26,6 +26,9 @@ object ErrorReporting { def errorTree(tree: untpd.Tree, msg: Message)(using Context): tpd.Tree = errorTree(tree, msg, tree.srcPos) + def errorTree(tree: untpd.Tree, msg: => String)(using Context): tpd.Tree = + errorTree(tree, msg.toMessage) + def errorTree(tree: untpd.Tree, msg: TypeError, pos: SrcPos)(using Context): tpd.Tree = tree.withType(errorType(msg, pos)) @@ -34,6 +37,9 @@ object ErrorReporting { ErrorType(msg) } + def errorType(msg: => String, pos: SrcPos)(using Context): ErrorType = + errorType(msg.toMessage, pos) + def errorType(ex: TypeError, pos: SrcPos)(using Context): ErrorType = { report.error(ex, pos) ErrorType(ex.toMessage) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 020027ea9d74..42c78dcfb32c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -447,7 +447,7 @@ object Implicits: /** An explanation of the cause of the failure as a string */ def explanation(using Context): String - def msg(using Context): Message = explanation + def msg(using Context): Message = explanation.toMessage /** If search was for an implicit conversion, a note describing the failure * in more detail - this is either empty or starts with a '\n' diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 3ca15ab976dd..1aa53d866b5e 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -464,7 +464,7 @@ object RefChecks { if (autoOverride(member) || other.owner.isAllOf(JavaInterface) && warnOnMigration( - "`override` modifier required when a Java 8 default method is re-implemented", + "`override` modifier required when a Java 8 default method is re-implemented".toMessage, member.srcPos, version = `3.0`)) member.setFlag(Override) else if (member.isType && self.memberInfo(member) =:= self.memberInfo(other)) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 48b0305de5e1..b90409e72364 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" + if tree.name == nme.CONSTRUCTOR then ex"$qualType does not have a constructor".toMessage 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 f97a2d183ce5..49a8d44134d2 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") + fail(em"reference to `$name` is ambiguous; it is imported twice".toMessage) found if selector.rename == termName && selector.rename != nme.WILDCARD then @@ -2665,7 +2665,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") + else i"package ${tree.pid.name} does not exist".toMessage) end typedPackageDef def typedAnnotated(tree: untpd.Annotated, pt: Type)(using Context): Tree = { diff --git a/compiler/test/dotty/tools/dotc/StringFormatterTest.scala b/compiler/test/dotty/tools/dotc/StringFormatterTest.scala index a5b5902b1f2e..e745fa515443 100644 --- a/compiler/test/dotty/tools/dotc/StringFormatterTest.scala +++ b/compiler/test/dotty/tools/dotc/StringFormatterTest.scala @@ -88,7 +88,7 @@ abstract class AbstractStringFormatterTest extends DottyTest: override def initializeCtx(fc: FreshContext) = super.initializeCtx(fc.setSetting(fc.settings.color, "never")) def Foo = newSymbol(defn.RootClass, typeName("Foo"), EmptyFlags, TypeBounds.empty).typeRef - def Err = newErrorSymbol(defn.RootClass, typeName("Err"), "") + def Err = newErrorSymbol(defn.RootClass, typeName("Err"), "".toMessage) def Big = (1 to 120).foldLeft(defn.StringType)((tp, i) => RefinedType(tp, typeName("A" * 69 + i), TypeAlias(defn.IntType))) def mkCstrd = diff --git a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala index 05f218059f02..44cf83b521f4 100644 --- a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala +++ b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala @@ -6,6 +6,7 @@ import Settings._ import org.junit.Test import org.junit.Assert._ +import core.Decorators.toMessage class ScalaSettingsTests: @@ -72,14 +73,14 @@ class ScalaSettingsTests: val proc = sets.processArguments(sumy, processAll = true, skipped = Nil) val conf = sets.Wconf.valueIn(proc.sstate) val sut = reporting.WConf.fromSettings(conf).getOrElse(???) - val msg = NoExplanation("There was a problem!") + val msg = "There was a problem!".toMessage val depr = new Diagnostic.DeprecationWarning(msg, util.NoSourcePosition) assertEquals(Action.Silent, sut.action(depr)) val feat = new Diagnostic.FeatureWarning(msg, util.NoSourcePosition) assertEquals(Action.Error, sut.action(feat)) val warn = new Diagnostic.Warning(msg, util.NoSourcePosition) assertEquals(Action.Warning, sut.action(warn)) - val nowr = new Diagnostic.Warning(NoExplanation("This is a problem."), util.NoSourcePosition) + val nowr = new Diagnostic.Warning("This is a problem.".toMessage, util.NoSourcePosition) assertEquals(Action.Silent, sut.action(nowr)) end ScalaSettingsTests