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 c49b7d9556c9..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 } } @@ -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) } } @@ -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 6714f664620b..b9d63c139e3b 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) } @@ -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 0884ec19b53e..a2807f41191a 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) @@ -309,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) @@ -613,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 1e1db19bcf25..59895d3c32a7 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 _ => @@ -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 @@ -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 { @@ -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/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 52325e36037d..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(em"cannot establish a reference to $res") + throw TypeError(em"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/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 899914e872c8..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 @@ -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/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/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/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..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. @@ -58,8 +58,11 @@ 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 = reporting.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. @@ -269,8 +272,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 @@ -282,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 } @@ -292,21 +297,16 @@ object Decorators { if (xs.head eq x1) && (xs.tail eq xs1) then xs else x1 :: xs1 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". - */ - def em(args: Shown*)(using Context): String = - forErrorMessages(new StringFormatter(sc).assemble(args)) - - /** Formatting with added explanations: Like `em`, but add explanations to - * give more info about type variables and to disambiguate where needed. + /** Interpolator yielding an error message, which undergoes + * the formatting defined in Message. */ - def ex(args: Shown*)(using Context): String = - explained(new StringFormatter(sc).assemble(args)) + def em(args: Shown*)(using Context): NoExplanation = + 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/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index f267e6c85e03..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 } 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/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index a4f1bf3c5e80..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) = - em"""Toplevel definition $name is defined in - | $chosen - |and also in - | $f""" + 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, @@ -2454,8 +2455,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/SymbolLoaders.scala b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala index c5ae98853061..9eb67b468cfa 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 @@ -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/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..b1885d3e9fba 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 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 + * 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 creationContext 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..0b1258d3801c 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 } } @@ -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: @@ -5299,18 +5304,16 @@ 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".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/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/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 34c22439a932..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 { @@ -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/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/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/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/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 1806cdfc909b..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 @@ -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/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/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 a198cccc85cc..33cf1a2722c9 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 ----------------------------------------------------------- */ @@ -1081,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 @@ -1234,7 +1222,7 @@ object Parsers { null } catch { - case ex: FromDigitsException => syntaxErrorOrIncomplete(ex.getMessage) + case ex: FromDigitsException => syntaxErrorOrIncomplete(ex.getMessage.toMessage) } Literal(Constant(value)) } @@ -1364,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), " ") @@ -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) @@ -1743,7 +1731,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)) @@ -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( @@ -1978,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()) @@ -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) } } @@ -2129,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) { @@ -2318,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() @@ -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()) } @@ -3488,7 +3476,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) } @@ -3529,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 @@ -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 } @@ -3943,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 } @@ -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..8f9ac582bc12 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,23 @@ object Scanners { */ var errOffset: Offset = NoOffset + /** Implements CharArrayReader's error method */ + 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 +163,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 +196,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 @@ -245,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), "`") @@ -603,9 +607,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 +632,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 +787,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 +869,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 +935,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,18 +972,18 @@ object Scanners { case SU => if (isAtEnd) token = EOF else { - error("illegal character") + error(em"illegal character") nextChar() } case _ => 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) @@ -994,7 +998,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 +1028,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 +1108,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 +1206,7 @@ object Scanners { nextChar() token = STRINGLIT } - else error("unclosed string literal") + else error(em"unclosed string literal") } private def getRawStringLit(): Unit = @@ -1216,7 +1220,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 +1290,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 +1298,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 +1452,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 +1535,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 3d9f5fb7ad6d..77c5a1bf376b 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 @@ -13,7 +14,7 @@ import Parsers._ import util.Spans._ import core._ import Constants._ -import Decorators.toMessage +import Decorators.{em, toMessage} import util.SourceFile import Utility._ @@ -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 } @@ -330,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) @@ -396,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 = { @@ -422,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/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/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/quoted/Interpreter.scala b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala index 5a9490c3723e..6c3d03fad4ca 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 */ @@ -46,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 } @@ -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)) { @@ -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/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..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,27 +48,33 @@ 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))) + def warning(msg: Message)(using Context): Unit = + warning(msg, NoSourcePosition) + 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: => 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)) @@ -91,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/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index d9140a6309b8..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 @@ -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 9e397d606491..a1fe6773c1d2 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -2,17 +2,35 @@ 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 = "" - +/** ## Tips for error message generation + * + * - 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, + * 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 + * 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 file `messages.scala` 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 val sourceStr = if version != null then i"-source $version" else "" @@ -22,7 +40,188 @@ 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) + + /** 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() = + seen.clear() + recordOK = false + + /** 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") + + /** 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 = 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 then + alts = entry :: existing + seen(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 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" + } + + 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 + } + + 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 + 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 */ + 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 @@ -39,9 +238,41 @@ object 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 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) { self => - import Message._ +abstract class Message(val errorId: ErrorMessageID)(using Context) { self => + 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 +283,27 @@ abstract class Message(val errorId: ErrorMessageID) { 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 = 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. + if addendum.isEmpty 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,61 +312,69 @@ abstract class Message(val errorId: ErrorMessageID) { self => */ def canExplain: Boolean = explain.nonEmpty - private var myMsg: String | Null = null private var myIsNonSensical: Boolean = false - 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 + /** 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 + + 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 = 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 + 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 /** Override with `true` for messages that should always be shown even if their @@ -143,10 +387,14 @@ abstract class Message(val errorId: ErrorMessageID) { 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` */ -class NoExplanation(msgFn: => String) 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/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/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 2197ea63a1c2..a3356c1f7021 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 @@ -40,211 +43,212 @@ import cc.CaptureSet.IdentityCaptRefMap * ``` */ - abstract class SyntaxMsg(errorId: ErrorMessageID) extends Message(errorId): - def kind = MessageKind.Syntax +abstract class SyntaxMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): + def kind = MessageKind.Syntax - abstract class TypeMsg(errorId: ErrorMessageID) extends Message(errorId): - def kind = MessageKind.Type +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*) +trait ShowMatchTrace(tps: Type*)(using Context) extends Message: + 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) - override def canExplain = true +abstract class TypeMismatchMsg(found: Type, expected: Type)(errorId: ErrorMessageID)(using Context) +extends Message(errorId), ShowMatchTrace(found, expected): + def kind = MessageKind.TypeMismatch + def explain(using Context) = err.whyNoMatchStr(found, expected) + override def canExplain = true - abstract class NamingMsg(errorId: ErrorMessageID) extends Message(errorId): - def kind = MessageKind.Naming +abstract class NamingMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId), NoDisambiguation: + def kind = MessageKind.Naming - abstract class DeclarationMsg(errorId: ErrorMessageID) extends Message(errorId): - def kind = MessageKind.Declaration +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): - def kind = MessageKind.NotFound - def name: Name +/** 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) extends Message(errorId): - def kind = MessageKind.PatternMatch +abstract class PatternMatchMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): + def kind = MessageKind.PatternMatch - abstract class CyclicMsg(errorId: ErrorMessageID) extends Message(errorId): - def kind = MessageKind.Cyclic +abstract class CyclicMsg(errorId: ErrorMessageID)(using Context) extends Message(errorId): + def kind = MessageKind.Cyclic - abstract class ReferenceMsg(errorId: ErrorMessageID) extends Message(errorId): - def kind = MessageKind.Reference +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 - - 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")}.""" +abstract class EmptyCatchOrFinallyBlock(tryBody: untpd.Tree, errNo: ErrorMessageID)(using Context) +extends SyntaxMsg(errNo) { + def explain(using Context) = { + val tryString = tryBody match { + case Block(Nil, untpd.EmptyTree) => "{}" + case _ => tryBody.show } - } - - 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""" - } - - 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.""" - } - - class DeprecatedWithOperator()(using Context) - extends SyntaxMsg(DeprecatedWithOperatorID) { - def msg = - em"""${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")}.""" - } - - class CaseClassMissingParamList(cdef: untpd.TypeDef)(using Context) - extends SyntaxMsg(CaseClassMissingParamListID) { - def msg = - em"""|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}.""" - } - 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 code1 = + s"""|import scala.util.control.NonFatal + | + |try $tryString catch { + | case NonFatal(e) => ??? + |}""".stripMargin - val inferred = - if (pt == WildcardType) "" - else i"\nWhat I could infer was: $pt" + val code2 = + s"""|try $tryString finally { + | // perform your cleanup here! + |}""".stripMargin - i"""Missing parameter type - | - |I could not infer the type of the parameter ${param.name}$ofFun.$inferred""" - } + 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: + | + |$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(using Context) = + 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) = + 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) = + i"""${hl("with")} as a type operator has been deprecated; use ${hl("&")} instead""" + def explain(using Context) = + 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")}.""" +} + +class CaseClassMissingParamList(cdef: untpd.TypeDef)(using Context) +extends SyntaxMsg(CaseClassMissingParamListID) { + def msg(using Context) = + i"""|A ${hl("case class")} must have at least one parameter list""" + + def explain(using Context) = + 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}.""" +} + +class AnonymousFunctionMissingParamType(param: untpd.ValDef, + tree: untpd.Function, + pt: Type) + (using Context) +extends TypeMsg(AnonymousFunctionMissingParamTypeID) { + def msg(using Context) = { + val ofFun = + if param.name.is(WildcardParamName) + || (MethodType.syntheticParamNames(tree.args.length + 1) contains param.name) + then i" of expanded function:\n$tree" + else "" - def explain = "" - } + val inferred = + if (pt == WildcardType) "" + else i"\nWhat I could infer was: $pt" - 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 + i"""Missing parameter type + | + |I could not infer the type of the parameter ${param.name}$ofFun.$inferred""" + } + + def explain(using Context) = "" +} + +class WildcardOnTypeArgumentNotAllowedOnNew()(using Context) +extends SyntaxMsg(WildcardOnTypeArgumentNotAllowedOnNewID) { + def msg(using Context) = "Type argument must be fully defined" + def explain(using Context) = + 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 - 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 """ - } + val code2: String = + """ + |object TyperDemo { + | class Team[A] + | val team = new Team[Int] + |} + """.stripMargin + i"""|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}" +// Type Errors ------------------------------------------------------------ // +class DuplicateBind(bind: untpd.Bind, tree: untpd.CaseDef)(using Context) +extends NamingMsg(DuplicateBindID) { + def msg(using Context) = i"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}" - } + def explain(using Context) = { + 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 body = tree.body match { + case Block(Nil, untpd.EmptyTree) => "" + case body => s" ${body.show}" + } - val caseDef = s"case $pat$guard => $body" + 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!""" - } + i"""|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 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.""" - } +class MissingIdent(tree: untpd.Ident, treeKind: String, val name: Name)(using Context) +extends NotFoundMsg(MissingIdentID) { + def msg(using Context) = i"Not found: $treeKind$name" + def explain(using Context) = { + 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.""" } +} - class TypeMismatch(found: Type, expected: Type, inTree: Option[untpd.Tree], addenda: => String*)(using Context) - extends TypeMismatchMsg(found, expected)(TypeMismatchID): +class TypeMismatch(found: Type, expected: Type, inTree: Option[untpd.Tree], addenda: => String*)(using Context) + extends TypeMismatchMsg(found, expected)(TypeMismatchID): + 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 @@ -272,2265 +276,2497 @@ import cc.CaptureSet.IdentityCaptRefMap case _ => mapOver(tp) - def msg = - 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 - - 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 - - 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 - |""" - } - } - - class TopLevelImplicitClass(cdef: untpd.TypeDef)(using Context) - extends SyntaxMsg(TopLevelImplicitClassID) { - def msg = em"""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 "" - 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}""" - } - } - - 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 explain = - em"""|Implicit classes may not be case classes. Instead use a plain class: - | - |implicit class ${cdef.name}... - | - |""" - } + 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 (foundStr, expectedStr) = Formatting.typeDiff(found2, expected2) + i"""|Found: $foundStr + |Required: $expectedStr""" + end msg + + 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 + +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(using Context) = { + 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 + "" - 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)" - em"""Implicit classes may only take one non-implicit argument in their constructor. For example: + 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("") + + i"$selected $name is not a member of ${site.widen}$finalAddendum" + } + + def explain(using Context) = "" +} + +class EarlyDefinitionsNotSupported()(using Context) +extends SyntaxMsg(EarlyDefinitionsNotSupportedID) { + def msg(using Context) = "Early definitions are not supported; use trait parameters instead" + + def explain(using Context) = { + val code1 = + """|trait Logging { + | val f: File + | f.open() + | onExit(f.close()) + | def log(msg: String) = f.write(msg) + |} | - | $example + |class B extends Logging { + | val f = new File("log.data") // triggers a NullPointerException + |} | - |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 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}""" - } - } - - class RepeatedModifier(modifier: String)(implicit ctx:Context) - extends SyntaxMsg(RepeatedModifierID) { - def msg = em"""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 - | - |""" - } - } - - 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)}\"" - em"""|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 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 => ... ")} - |""" - } - - class IllegalStartSimpleExpr(illegalToken: String)(using Context) - extends SyntaxMsg(IllegalStartSimpleExprID) { - def msg = em"expression expected but ${Red(illegalToken)} found" - def explain = { - em"""|An expression cannot start with ${Red(illegalToken)}.""" - } - } + |// 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 - class MissingReturnType()(implicit ctx:Context) - 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""" + 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: + | + |$code1 + | + |The above code can now be written as: + | + |$code2 + |""" } - - class MissingReturnTypeWithReturnStatement(method: Symbol)(using Context) - extends SyntaxMsg(MissingReturnTypeWithReturnStatementID) { - def msg = em"$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")}""" +} + +class TopLevelImplicitClass(cdef: untpd.TypeDef)(using Context) +extends SyntaxMsg(TopLevelImplicitClassID) { + 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 + 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 "" + 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. + | | + |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 YieldOrDoExpectedInForComprehension()(using Context) - extends SyntaxMsg(YieldOrDoExpectedInForComprehensionID) { - def msg = em"${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")} - | - |""" - } +class ImplicitCaseClass(cdef: untpd.TypeDef)(using Context) +extends SyntaxMsg(ImplicitCaseClassID) { + def msg(using Context) = i"""A ${hl("case class")} may not be defined as ${hl("implicit")}""" - class ProperDefinitionNotFound()(using Context) - extends Message(ProperDefinitionNotFoundID) { - def kind = MessageKind.DocComment - def msg = em"""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 - - 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.""" - } + def explain(using Context) = + i"""|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(using Context) = "Implicit classes must accept exactly one primary constructor parameter" + def explain(using Context) = { + val example = "implicit class RichDate(date: java.util.Date)" + i"""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 ByNameParameterNotSupported(tpe: untpd.Tree)(using Context) - extends SyntaxMsg(ByNameParameterNotSupportedID) { - def msg = em"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...)")} - |""" - } +class ObjectMayNotHaveSelfType(mdef: untpd.ModuleDef)(using Context) +extends SyntaxMsg(ObjectMayNotHaveSelfTypeID) { + def msg(using Context) = i"""${hl("object")}s must not have a self ${hl("type")}""" - 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 - em"""|$msgPrefix type arguments for $prettyName$expectedArgString - |expected: $expectedArgString - |actual: $actualArgString""".stripMargin - - def explain = { - val tooManyTypeParams = - """|val tuple2: (Int, String) = (1, "one") - |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}""" - else - em"""|You have not supplied enough type parameters - |If you specify one type parameter then you need to specify every type parameter.""" - } + def explain(using Context) = { + val untpd.ModuleDef(name, tmpl) = mdef + val ValDef(_, selfTpt, _) = tmpl.self + 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 + | - Let the object extend a trait containing the self type: + | + | object $name extends ${selfTpt.show}""" } +} - class IllegalVariableInPatternAlternative(name: Name)(using Context) - extends SyntaxMsg(IllegalVariableInPatternAlternativeID) { - def msg = em"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 - - 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""" - } - } +class RepeatedModifier(modifier: String)(implicit ctx:Context) +extends SyntaxMsg(RepeatedModifierID) { + def msg(using Context) = i"""Repeated modifier $modifier""" - class IdentifierExpected(identifier: String)(using Context) - 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 - | - |""" - } + def explain(using Context) = { + val code1 = "private private val Origin = Point(0, 0)" + val code2 = "private final val Origin = Point(0, 0)" + i"""This happens when you accidentally specify the same modifier twice. + | + |Example: + | + |$code1 + | + |instead of + | + |$code2 + | + |""" } +} - class AuxConstructorNeedsNonImplicitParameter()(implicit ctx:Context) - 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 - |""" +class InterpolatedStringError()(implicit ctx:Context) +extends SyntaxMsg(InterpolatedStringErrorID) { + 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)}\"" + i"""|This usually happens when you forget to place your expressions inside curly braces. + | + |$code1 + | + |should be written as + | + |$code2 + |""" } +} - 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 - |""" - } +class UnboundPlaceholderParameter()(implicit ctx:Context) +extends SyntaxMsg(UnboundPlaceholderParameterID) { + def msg(using Context) = i"""Unbound placeholder parameter; incorrect use of ${hl("_")}""" + def explain(using Context) = + 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")}) + |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 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 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 "" - em"""|${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 - |""" +class IllegalStartSimpleExpr(illegalToken: String)(using Context) +extends SyntaxMsg(IllegalStartSimpleExprID) { + def msg(using Context) = i"expression expected but ${Red(illegalToken)} found" + def explain(using Context) = { + i"""|An expression cannot start with ${Red(illegalToken)}.""" } +} - 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`. - |""" - } +class MissingReturnType()(implicit ctx:Context) +extends SyntaxMsg(MissingReturnTypeID) { + def msg(using Context) = "Missing return type" + def explain(using Context) = + i"""|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(using Context) = i"$method has a return statement; it needs a result type" + def explain(using Context) = + 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")}""" +} - class MatchCaseUnreachable()(using Context) - extends Message(MatchCaseUnreachableID) { - def kind = MessageKind.MatchCaseUnreachable - def msg = "Unreachable case" - def explain = "" - } +class YieldOrDoExpectedInForComprehension()(using Context) +extends SyntaxMsg(YieldOrDoExpectedInForComprehensionID) { + def msg(using Context) = i"${hl("yield")} or ${hl("do")} expected" - 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 explain = "" - } + def explain(using Context) = + 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. + | + |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(using Context) = i"""Proper definition was not found in ${hl("@usecase")}""" + + def explain(using Context) = { + 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 MatchableWarning(tp: Type, pattern: Boolean)(using Context) - 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""" - - 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 { ... }""" - else - em"""The value can be converted to a `Matchable` by appending `.asMatchable`. - |`asMatchable` needs to be imported from scala.compiletime.""" + 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 + | + |${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 SeqWildcardPatternPos()(using Context) - extends SyntaxMsg(SeqWildcardPatternPosID) { - def msg = em"""${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""" - } - } +class ByNameParameterNotSupported(tpe: untpd.Tree)(using Context) +extends SyntaxMsg(ByNameParameterNotSupportedID) { + def msg(using Context) = i"By-name parameter type ${tpe} not allowed here." - 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 explain(using Context) = + 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: + |${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(using Context) = + 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 + i"""|$msgPrefix type arguments for $prettyName$expectedArgString + |expected: $expectedArgString + |actual: $actualArgString""" + + def explain(using Context) = { + val tooManyTypeParams = + """|val tuple2: (Int, String) = (1, "one") + |val list: List[(Int, String)] = List(tuple2)""".stripMargin + + if (actualCount > expectedCount) + i"""|You have supplied too many type parameters | - |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 + |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 + 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) = i"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 | 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]]")} - |""" - } - } - - class PkgDuplicateSymbol(existing: Symbol)(using Context) - extends NamingMsg(PkgDuplicateSymbolID) { - def msg = em"Trying to define package with same name as $existing" - def explain = "" - } + |}""".stripMargin - class ExistentialTypesNoLongerSupported()(using Context) - extends SyntaxMsg(ExistentialTypesNoLongerSupportedID) { - def msg = - em"""|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[?]")} - |""" - } + val fixedVarInAlternative = + """|def g(pair: (Int,Int)): Int = pair match { + | case (1, n) => n + | case (n, 1) => n + | case _ => 0 + |}""".stripMargin - 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")} - |""" + 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: + | + |$varInAlternative + |could be implemented by moving each alternative into a separate case: + | + |$fixedVarInAlternative""" + } +} + +class IdentifierExpected(identifier: String)(using Context) +extends SyntaxMsg(IdentifierExpectedID) { + def msg(using Context) = "identifier expected" + def explain(using Context) = { + 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: + | + |$wrongIdentifier + | + |Write your code like: + | + |$validIdentifier + | + |""" } +} - class OverridesNothing(member: Symbol)(using Context) - extends DeclarationMsg(OverridesNothingID) { - def msg = em"""${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? - |""" - } +class AuxConstructorNeedsNonImplicitParameter()(implicit ctx:Context) +extends SyntaxMsg(AuxConstructorNeedsNonImplicitParameterID) { + def msg(using Context) = "Auxiliary constructor needs non-implicit parameter list" + def explain(using Context) = + 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. + | + |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(using Context) = "Illegal literal" + def explain(using Context) = + 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 + | - 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(using Context) = i"""|Widening conversion from $sourceType to $targetType loses precision. + |Write `.to$targetType` instead.""" + 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(using Context) = + val addendum = if hasMore then "(More unmatched cases are elided)" else "" + i"""|${hl("match")} may not be exhaustive. + | + |It would fail on pattern case: $uncovered + |$addendum""" - 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" - em"""${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} - |""" - } - class OverrideError(override val msg: String) 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 = em"${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 + def explain(using Context) = + 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 + |""" +} + +class UncheckedTypePattern(msgFn: => String)(using Context) + extends PatternMatchMsg(UncheckedTypePatternID) { + def msg(using Context) = msgFn + def explain(using Context) = + 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`. + |""" +} + +class MatchCaseUnreachable()(using Context) +extends Message(MatchCaseUnreachableID) { + def kind = MessageKind.MatchCaseUnreachable + def msg(using Context) = "Unreachable case" + def explain(using Context) = "" +} + +class MatchCaseOnlyNullWarning()(using Context) +extends PatternMatchMsg(MatchCaseOnlyNullWarningID) { + 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) = "" +} + +class MatchableWarning(tp: Type, pattern: Boolean)(using Context) +extends TypeMsg(MatchableWarningID) { + def msg(using Context) = + val kind = if pattern then "pattern selector" else "value" + i"""${kind} should be an instance of Matchable,, + |but it has unmatchable type $tp instead""" + + def explain(using Context) = + if pattern then + 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 + |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 + 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) = 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 + |}""" + 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: + | + |$code + | + |Calling: + | + |${hl("sumOfTheFirstTwo(List(1, 2, 10))")} + | + |would give 3 as a result""" + } +} + +class IllegalStartOfSimplePattern()(using Context) +extends SyntaxMsg(IllegalStartOfSimplePatternID) { + def msg(using Context) = "pattern expected" + def explain(using Context) = { + 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 + |}""" + 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 + | 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(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) = + i"""|Existential types are no longer supported - + |use a wildcard or dependent type instead""" + def explain(using Context) = + i"""|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(using Context) = "Unbound wildcard type" + def explain(using Context) = + 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")}. + | + |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 ExpectedTokenButFound(expected: Token, found: Token)(using Context) - extends SyntaxMsg(ExpectedTokenButFoundID) { +class OverridesNothing(member: Symbol)(using Context) +extends DeclarationMsg(OverridesNothingID) { + def msg(using Context) = i"""${member} overrides nothing""" - private lazy val foundText = Tokens.showToken(found) + def explain(using Context) = + 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? + |""" +} + +class OverridesNothingButNameExists(member: Symbol, existing: List[Denotations.SingleDenotation])(using Context) +extends DeclarationMsg(OverridesNothingButNameExistsID) { + def msg(using Context) = + val what = + if !existing.exists(_.symbol.hasTargetName(member.targetName)) + then "target name" + else "signature" + i"""${member} has a different $what than the overridden declaration""" + def explain(using Context) = + val existingDecl: String = existing.map(_.showDcl).mkString(" \n") + 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} + | + |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), NoDisambiguation: + def msg(using Context) = + 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 "" + 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(using Context) = + if canExplain then err.whyNoMatchStr(memberTp, otherTp) else "" + +class ForwardReferenceExtendsOverDefinition(value: Symbol, definition: Symbol)(using Context) +extends ReferenceMsg(ForwardReferenceExtendsOverDefinitionID) { + def msg(using Context) = i"${definition.name} is a forward reference extending over the definition of ${value.name}" + + def explain(using Context) = + 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 + |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. + |""" +} - def msg = - val expectedText = - if (Tokens.isIdentifier(expected)) "an identifier" - else Tokens.showToken(expected) - em"""${expectedText} expected, but ${foundText} found""" +class ExpectedTokenButFound(expected: Token, found: Token)(using Context) +extends SyntaxMsg(ExpectedTokenButFoundID) { - 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 - "" - } + private def foundText = Tokens.showToken(found) - 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" - em"${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 - } + def msg(using Context) = + val expectedText = + if (Tokens.isIdentifier(expected)) "an identifier" + else Tokens.showToken(expected) + i"""${expectedText} expected, but ${foundText} found""" - 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 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 - } - - class UnreducibleApplication(tycon: Type)(using Context) extends TypeMsg(UnreducibleApplicationID): - def msg = em"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.""" - - class OverloadedOrRecursiveMethodNeedsResultType(cycleSym: Symbol)(using Context) - extends CyclicMsg(OverloadedOrRecursiveMethodNeedsResultTypeID) { - def msg = em"""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 - } - - class RecursiveValueNeedsResultType(cycleSym: Symbol)(using Context) - extends CyclicMsg(RecursiveValueNeedsResultTypeID) { - def msg = em"""Recursive $cycleSym needs type""" - def explain = - em"""The definition of $cycleSym is recursive and you need to specify its type. - |""".stripMargin - } - - 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" - 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 - } + 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 + else + "" +} + +class MixedLeftAndRightAssociativeOps(op1: Name, op2: Name, op2LeftAssoc: Boolean)(using Context) +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" + 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: + |${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(using Context) = i"""${cls.name} is ${traitOrAbstract}; it cannot be instantiated""" + def explain(using Context) = + 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 + | ${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(using Context) = i"unreducible application of higher-kinded type $tycon to wildcard arguments" + def explain(using Context) = + 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) = i"""Overloaded or recursive $cycleSym needs return type""" + def explain(using Context) = + 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. + | + |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 CyclicReferenceInvolvingImplicit(cycleSym: Symbol)(using Context) - extends CyclicMsg(CyclicReferenceInvolvingImplicitID) { - def msg = em"""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 - } +class RecursiveValueNeedsResultType(cycleSym: Symbol)(using Context) +extends CyclicMsg(RecursiveValueNeedsResultTypeID) { + def msg(using Context) = i"""Recursive $cycleSym needs type""" + def explain(using Context) = + i"""The definition of $cycleSym is recursive and you need to specify its type. + |""" +} + +class CyclicReferenceInvolving(denot: SymDenotation)(using Context) +extends CyclicMsg(CyclicReferenceInvolvingID) { + def msg(using Context) = + val where = if denot.exists then s" involving $denot" else "" + i"Cyclic reference$where" + def explain(using Context) = + 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. + |""" +} + +class CyclicReferenceInvolvingImplicit(cycleSym: Symbol)(using Context) +extends CyclicMsg(CyclicReferenceInvolvingImplicitID) { + def msg(using Context) = i"""Cyclic reference involving implicit $cycleSym""" + def explain(using Context) = + 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. + |""" +} - class SkolemInInferred(tree: tpd.Tree, pt: Type, argument: tpd.Tree)(using Context) - extends TypeMsg(SkolemInInferredID): - private def argStr = +class SkolemInInferred(tree: tpd.Tree, pt: Type, argument: tpd.Tree)(using Context) +extends TypeMsg(SkolemInInferredID): + def msg(using Context) = + def argStr = 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.""" - 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.""" - - class SuperQualMustBeParent(qual: untpd.Ident, cls: ClassSymbol)(using Context) - extends ReferenceMsg(SuperQualMustBeParentID) { - def msg = em"""|$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 - } - - class VarArgsParamMustComeLast()(using Context) - extends SyntaxMsg(VarArgsParamMustComeLastID) { - def msg = em"""${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. - |""" - } - - 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 = "") = { - 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 ${em"${whereFound.importInfo}"}""" - } else - ex"""$howVisible$qualifier in ${em"${whereFound.owner}"}""" - } - - def msg = - i"""|Reference to ${em"$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"} } - |""" - } - - class MethodDoesNotTakeParameters(tree: tpd.Tree)(using Context) - extends TypeMsg(MethodDoesNotTakeParametersId) { - def methodSymbol: 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" - em"$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 + 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) = + 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 + |relying on implicit search at this point.""" + +class SuperQualMustBeParent(qual: untpd.Ident, cls: ClassSymbol)(using Context) +extends ReferenceMsg(SuperQualMustBeParentID) { + 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 + 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: + |${parents.mkString(" - ", "\n - ", "")} + |""" +} + +class VarArgsParamMustComeLast()(using Context) +extends SyntaxMsg(VarArgsParamMustComeLastID) { + def msg(using Context) = i"""${hl("varargs")} parameter must come last""" + def explain(using Context) = + 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. + |""" +} + +import typer.Typer.BindingPrec + +class AmbiguousReference(name: Name, newPrec: BindingPrec, prevPrec: BindingPrec, prevCtx: Context)(using Context) + extends ReferenceMsg(AmbiguousReferenceID), NoDisambiguation { + + /** 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) } - - } - - 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 = - 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")} - |""" - } - - 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 - } - - class TypeDoesNotTakeParameters(tpe: Type, params: List[untpd.Tree])(using Context) - extends TypeMsg(TypeDoesNotTakeParametersID) { - private def fboundsAddendum = - 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 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 - |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 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("}")} - |""" - } - - 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}" - 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 = em"Missing type $parameters for $tpe" - def explain = em"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 - em"Type argument ${tpe} does not overlap with $which bound $bound" - else - em"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""" - } - - 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""" - } - - 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""" - - 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 explain = "" - } - - class TypesAndTraitsCantBeImplicit()(using Context) - extends SyntaxMsg(TypesAndTraitsCantBeImplicitID) { - def msg = em"""${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 = em"""${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 explain = "" - } - - class TraitsMayNotBeFinal(sym: Symbol)( - implicit ctx: Context) - extends SyntaxMsg(TraitsMayNotBeFinalID) { - def msg = em"""$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 = em"""${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 explain = "" - } - - class OnlyClassesCanHaveDeclaredButUndefinedMembers(sym: Symbol)( - implicit ctx: Context) - extends SyntaxMsg(OnlyClassesCanHaveDeclaredButUndefinedMembersID) { - - private def varNote = - if (sym.is(Mutable)) "Note that variables need to be initialized to be defined." + if (prec.isImportPrec) { + i"""$howVisible$qualifier by ${whereFound.importInfo}""" + } else + i"""$howVisible$qualifier in ${whereFound.owner}""" + } + + def msg(using Context) = + i"""|Reference to $name is ambiguous, + |it is both ${bindingString(newPrec, ctx)} + |and ${bindingString(prevPrec, prevCtx, " subsequently")}""" + + def explain(using Context) = + 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. + |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(using Context) = { + val more = if (tree.isInstanceOf[tpd.Apply]) " more" else "" + val meth = methodSymbol + val methStr = if (meth.exists) meth.showLocated else "expression" + i"$methStr does not take$more parameters" + } + + def explain(using Context) = { + val isNullary = methodSymbol.info.isInstanceOf[ExprType] + val addendum = + if (isNullary) "\nNullary methods may not be called with parenthesis" else "" - def msg = em"""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 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")}. - |""" + "You have specified more parameter lists than defined in the method definition(s)." + addendum } - 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 explain = "" - } +} - class CannotExtendContextFunction(sym: Symbol)(using Context) - extends SyntaxMsg(CannotExtendFunctionID) { - def msg = em"""$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 explain = "" - } - - class CannotHaveSameNameAs(sym: Symbol, cls: Symbol, reason: CannotHaveSameNameAs.Reason)(using Context) - extends SyntaxMsg(CannotHaveSameNameAsID) { - import CannotHaveSameNameAs._ - def reasonMessage: 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 = em"""$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 = em"""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 explain = "" - } - - class ValueClassesMayNotDefineASecondaryConstructor(valueClass: Symbol, constructor: Symbol)(using Context) - extends SyntaxMsg(ValueClassesMayNotDefineASecondaryConstructorID) { - def msg = em"""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 explain = "" - } - - class ValueClassesMayNotBeAbstract(valueClass: Symbol)(using Context) - extends SyntaxMsg(ValueClassesMayNotBeAbstractID) { - def msg = em"""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 = em"""A value class parameter may not be a ${hl("var")}""" - def explain = - em"""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 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 = em"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 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 = - em"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" - } - - class NoReturnFromInlineable(owner: Symbol)(using Context) - extends SyntaxMsg(NoReturnFromInlineableID) { - def msg = em"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. - |""" - } - - class ReturnOutsideMethodDefinition(owner: Symbol)(using Context) - extends SyntaxMsg(ReturnOutsideMethodDefinitionID) { - def msg = em"${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. - |""" - } - - class ExtendFinalClass(clazz:Symbol, finalClazz: Symbol)(using Context) - extends SyntaxMsg(ExtendFinalClassID) { - def msg = em"$clazz cannot extend ${hl("final")} $finalClazz" - def explain = - em"""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 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")}. - |""" - } - - 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" - 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}""" - } - - 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" - - 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 - 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.""" - 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.""" - } - - 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 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")}""" - } - - class MissingEmptyArgumentList(method: String)(using Context) - extends SyntaxMsg(MissingEmptyArgumentListID) { - def msg = em"$method must be called with ${hl("()")} argument" - def explain = { - val codeExample = - """def next(): T = ... - |next // is expanded to next()""" +class AmbiguousOverload(tree: tpd.Tree, val alternatives: List[SingleDenotation], pt: Type, addendum: String = "")( + implicit ctx: Context) +extends ReferenceMsg(AmbiguousOverloadID), NoDisambiguation { + private def all = if (alternatives.length == 2) "both" else "all" + def msg(using Context) = + i"""|Ambiguous overload. The ${err.overloadedAltsStr(alternatives)} + |$all match ${err.expectedTypeStr(pt)}$addendum""" + def explain(using Context) = + 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 + |- adding a type ascription as in ${hl("instance.myMethod: String => Int")} + |""" +} + +class ReassignmentToVal(name: Name)(using Context) + extends TypeMsg(ReassignmentToValID) { + def msg(using Context) = i"""Reassignment to val $name""" + def explain(using Context) = + 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 + |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(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 $tpe, which is not + |declared to take any. + |""" +} + +class VarValParametersMayNotBeCallByName(name: TermName, mutable: Boolean)(using Context) + extends SyntaxMsg(VarValParametersMayNotBeCallByNameID) { + 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) = + 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) {"} + | ${s" def $name() = ${name}Tick"} + | ${hl("}")} + |""" +} + +class MissingTypeParameterFor(tpe: Type)(using Context) + extends SyntaxMsg(MissingTypeParameterForID) { + def msg(using Context) = + 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) = "" +} + +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) = 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) + extends TypeMsg(MissingArgumentID): + 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(using Context) = "" + +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(using Context) = + if isBounds then + i"Type argument ${tpe} does not overlap with $which bound $bound" + else + 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) = 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) = 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) = + i"""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(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) = i"""${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(using Context) = "" + 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) = 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) = 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." +} + +class NativeMembersMayNotHaveImplementation(sym: Symbol)( + implicit ctx: Context) + extends SyntaxMsg(NativeMembersMayNotHaveImplementationID) { + 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) = i"""A trait cannot define a ${hl("@native")} method.""" + def explain(using Context) = "" +} + +class OnlyClassesCanHaveDeclaredButUndefinedMembers(sym: Symbol)( + implicit ctx: Context) + extends SyntaxMsg(OnlyClassesCanHaveDeclaredButUndefinedMembersID) { + + 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 "" +} + +class CannotExtendAnyVal(sym: Symbol)(using Context) + extends SyntaxMsg(CannotExtendAnyValID) { + def msg(using Context) = i"""$sym cannot extend ${hl("AnyVal")}""" + def explain(using Context) = + 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")}. + |""" +} - 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.""" - } +class CannotExtendJavaEnum(sym: Symbol)(using Context) + extends SyntaxMsg(CannotExtendJavaEnumID) { + 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 DuplicateNamedTypeParameter(name: Name)(using Context) - extends SyntaxMsg(DuplicateNamedTypeParameterID) { - def msg = em"Type parameter $name was defined multiple times." - def explain = "" +class CannotExtendContextFunction(sym: Symbol)(using Context) + extends SyntaxMsg(CannotExtendFunctionID) { + def msg(using Context) = i"""$sym cannot extend a context function class""" + def explain(using Context) = "" } - 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 explain = "" +class JavaEnumParentArgs(parent: Type)(using Context) + extends TypeMsg(JavaEnumParentArgsID) { + def msg(using Context) = i"""not enough arguments for constructor Enum: ${hl("(name: String, ordinal: Int)")}: ${hl(parent.show)}""" + def explain(using Context) = "" } - 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 CannotHaveSameNameAs(sym: Symbol, cls: Symbol, reason: CannotHaveSameNameAs.Reason)(using Context) + extends NamingMsg(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 TraitIsExpected(symbol: Symbol)(using Context) extends SyntaxMsg(TraitIsExpectedID) { - def msg = em"$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 + def msg(using Context) = i"""$sym cannot have the same name as ${cls.showLocated} -- """ + reasonMessage + def explain(using Context) = "" +} +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(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) = 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) = 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) = 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) = i"""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(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(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(using Context) = i"""A value class parameter may not be a ${hl("var")}""" + def explain(using Context) = + i"""A value class must have exactly one ${hl("val")} parameter.""" +} + +class ValueClassNeedsOneValParam(valueClass: Symbol)(using Context) + extends SyntaxMsg(ValueClassNeedsExactlyOneValParamID) { + def msg(using Context) = i"""Value class needs one ${hl("val")} parameter""" + def explain(using Context) = "" +} + +class ValueClassParameterMayNotBeCallByName(valueClass: Symbol, param: Symbol)(using Context) + extends SyntaxMsg(ValueClassParameterMayNotBeCallByNameID) { + 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(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) = 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 + | - 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(using Context) = s"Wrong number of parameters, expected: $expected" + def explain(using Context) = "" +} + +class DuplicatePrivateProtectedQualifier()(using Context) + extends SyntaxMsg(DuplicatePrivateProtectedQualifierID) { + def msg(using Context) = "Duplicate private/protected qualifier" + def explain(using Context) = + 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) = + 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) = i"No explicit ${hl("return")} allowed from inlineable $owner" + def explain(using Context) = + 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. + |""" +} + +class ReturnOutsideMethodDefinition(owner: Symbol)(using Context) + extends SyntaxMsg(ReturnOutsideMethodDefinitionID) { + def msg(using Context) = i"${hl("return")} outside method definition" + def explain(using Context) = + 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) = i"$clazz cannot extend ${hl("final")} $finalClazz" + def explain(using Context) = + 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) = i"${hl("=")}, ${hl(">:")}, or ${hl("<:")} expected, but ${Tokens.showToken(found)} found" + + def explain(using Context) = + 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. + | + |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(using Context) = + val name = cls.name.stripModuleClassSuffix + i"Name clash: both ${cls.owner} and its companion object defines $name" + def explain(using Context) = + 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}""" +} + +class TailrecNotApplicable(symbol: Symbol)(using Context) + extends SyntaxMsg(TailrecNotApplicableID) { + def msg(using Context) = { + val reason = + 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" + } + 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(using Context) = + val originalType = ctx.printer.dclsText(boundSyms, "; ").show + 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 + |type used instead: $tp2 + |This choice can cause follow-on type errors or hide type errors. + |Proceed at own risk.""" + def explain(using Context) = + 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.""" +} + +class OnlyFunctionsCanBeFollowedByUnderscore(tp: Type)(using Context) + extends SyntaxMsg(OnlyFunctionsCanBeFollowedByUnderscoreID) { + def msg(using Context) = i"Only function types can be followed by ${hl("_")} but the current expression has type $tp" + def explain(using Context) = + 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) = i"$method must be called with ${hl("()")} argument" + def explain(using Context) = { + val codeExample = + """def next(): T = ... + |next // is expanded to next()""" + + i"""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(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) = i"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(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(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(using Context) = i"$symbol is not a trait" + def explain(using Context) = { + 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 - 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 - |""" - } + i"""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 explain = "" - } +class TraitRedefinedFinalMethodFromAnyRef(method: Symbol)(using Context) extends SyntaxMsg(TraitRedefinedFinalMethodFromAnyRefID) { + def msg(using Context) = i"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): - private def where: String = +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}" else if conflicting.owner == owner then "" else i" in ${conflicting.owner}" - private def note = + 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 = - if conflicting.isTerm != name.isTermName then - em"$name clashes with $conflicting$where; the two must be defined together" - else - em"$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. - |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.""" - } - - 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 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 - } - - 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 "" - em"""| ${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 + if conflicting.isTerm != name.isTermName then + i"$name clashes with $conflicting$where; the two must be defined together" 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 - } - - 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 explain = - em"${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 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 ")} - | ... - |}""" - 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 + 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}" + 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" + 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) = i"Wrong number of argument patterns for $qual; expected: ($argTypes%, %)" + def explain(using Context) = + i"""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(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 "" + 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") + 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")} + | - 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 + 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]]")}): + | + |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(using Context) = i"${hl("@static")} $member in ${member.owner} must be defined inside a static ${hl("object")}." + def explain(using Context) = + 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) = 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} { + | @static ${member} = ... + | ${nonStatics.map(m => s"${m.symbol} = ...").mkString("\n ")} + | ... + |}""" + 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. + | + |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(using Context) = i"Cyclic inheritance: $symbol extends itself$addendum" + def explain(using Context) = { + val codeExample = "class A extends A" + + i"""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(using Context) = { + 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") + + 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.""" + } + + def explain(using Context) = "" +} + +class UnableToExtendSealedClass(pclazz: Symbol)(using Context) extends SyntaxMsg(UnableToExtendSealedClassID) { + 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) = i"${symbol.showLocated} has an unparsable version number: $errorMessage" + def explain(using Context) = + 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 SymbolChangedSemanticsInVersion( + symbol: Symbol, + migrationVersion: ScalaVersion, + migrationMessage: String +)(using Context) extends SyntaxMsg(SymbolChangedSemanticsInVersionID) { + def msg(using Context) = i"${symbol.showLocated} has changed semantics in version $migrationVersion: $migrationMessage" + def explain(using Context) = + 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) = i"Could not emit switch for ${hl("@switch")} annotated match" + def explain(using Context) = { + 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 + + 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: + | + |$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(using Context) = i"${member.owner} does not have a companion class" + def explain(using Context) = + 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) = i"Polymorphic refinement $rsym without matching type in parent $parentSym is no longer allowed" + def explain(using Context) = + 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) = 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) = + val kind = + 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) = "" +} + +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 = + 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" + } + } + 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}" } - } - - class CyclicInheritance(symbol: Symbol, addendum: => String)(using Context) extends SyntaxMsg(CyclicInheritanceID) { - def msg = em"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""" - } - } - - 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") - - 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.""" - } - - 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 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 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.""" - } - - class SymbolChangedSemanticsInVersion( - symbol: Symbol, - migrationVersion: ScalaVersion, - migrationMessage: String - )(using Context) extends SyntaxMsg(SymbolChangedSemanticsInVersionID) { - def msg = em"${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.""" - } - - class UnableToEmitSwitch()(using Context) - extends SyntaxMsg(UnableToEmitSwitchID) { - def msg = em"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 + 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" - 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""" + atPhase(typerPhase) { + i"""$clashDescription: + |${previousDecl.showDcl} ${symLocation(previousDecl)} and + |${decl.showDcl} ${symLocation(decl)} + |""" + } + details + } + def explain(using Context) = "" +} + +class ImportRenamedTwice(ident: untpd.Ident)(using Context) extends SyntaxMsg(ImportRenamedTwiceID) { + 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(using Context) = + s"This type test will never return a result since the scrutinee type ${scrutTp.show} does not contain any value." + def explain(using Context) = "" +} + +// Relative of CyclicReferenceInvolvingImplicit and RecursiveValueNeedsResultType +class TermMemberNeedsResultTypeForImplicitSearch(cycleSym: Symbol)(using Context) + extends CyclicMsg(TermMemberNeedsNeedsResultTypeForImplicitSearchID) { + def msg(using Context) = i"""$cycleSym needs result type because its right-hand side attempts implicit search""" + def explain(using Context) = + 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) = 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) = i"$tree cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method" + def explain(using Context) = + 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)]")} + | + |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(using Context) = i"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(using Context) = "A pure expression does nothing in statement position; you may be omitting necessary parentheses" + def explain(using Context) = + 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) = i"Companion of traits cannot define mutable @static fields" + def explain(using Context) = "" +} + +class LazyStaticField()(using Context) + extends SyntaxMsg(LazyStaticFieldID) { + 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) = i"${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(using Context) = "Refinements cannot introduce overloaded definitions" + def explain(using Context) = + 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) = + 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) = + i"""Stable identifier required, but $tree found""" + 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(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` + 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") } - } - - class MissingCompanionForStatic(member: Symbol)(using Context) - extends SyntaxMsg(MissingCompanionForStaticID) { - def msg = em"${member.owner} does not have a companion class" - def explain = - em"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 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.""" - } + 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) = "" +} + +class TraitParameterUsedAsParentPrefix(cls: Symbol)(using Context) + extends DeclarationMsg(TraitParameterUsedAsParentPrefixID) { + def msg(using Context) = + s"${cls.show} cannot extend from a parent that is derived via its own parameters" + def explain(using Context) = + 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) = + i"""no enclosing class or object is named '${hl(name.show)}'""" + def explain(using Context) = + 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 + |current scope. + """ + } + +class IllegalCyclicTypeReference(sym: Symbol, where: String, lastChecked: Type)(using Context) + extends CyclicMsg(IllegalCyclicTypeReferenceID) { + 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(using Context) = "" +} + +class ErasedTypesCanOnlyBeFunctionTypes()(using Context) + extends SyntaxMsg(ErasedTypesCanOnlyBeFunctionTypesID) { + 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(using Context) = + i"""|A ${hl("case class")} must have at least one leading non-implicit parameter list""" + + def explain(using Context) = + 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}.""" +} + +class EnumerationsShouldNotBeEmpty(cdef: untpd.TypeDef)(using Context) + extends SyntaxMsg(EnumerationsShouldNotBeEmptyID) { + def msg(using Context) = "Enumerations must contain at least one case" + + def explain(using Context) = + i"""|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(using Context) = i"explicit extends clause needed because both enum case and enum class have type parameters" + + def explain(using Context) = + 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] { + | ${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(using Context) = i"illegal redefinition of standard $kindType $name" + def explain(using Context) = + 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) = i"No extension method allowed here, since collective parameters are given" + def explain(using Context) = + i"""|Extension method: + | `${mdef}` + |is defined inside an extension clause which has collective parameters. + |""" +} - 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 explain = "" - } +class ExtensionMethodCannotHaveTypeParams(mdef: untpd.DefDef)(using Context) + extends SyntaxMsg(ExtensionMethodCannotHaveTypeParamsID) { + def msg(using Context) = i"Extension method cannot have type parameters since some were already given previously" - 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)}" + def explain(using Context) = + 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. + |""" +} + +class ExtensionCanOnlyHaveDefs(mdef: untpd.Tree)(using Context) + extends SyntaxMsg(ExtensionCanOnlyHaveDefsID) { + def msg(using Context) = i"Only methods allowed here, since collective parameters are given" + def explain(using Context) = + 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) = i"Unexpected pattern for summonFrom. Expected ${hl("`x: T`")} or ${hl("`_`")}" + def explain(using Context) = + 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: + | 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(using Context) = i"anonymous instance must implement a type or have at least one extension method" + def explain(using Context) = + 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) = 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) = 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) = 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) = "" +} + +class CaseClassInInlinedCode(tree: tpd.Tree)(using Context) + extends SyntaxMsg(CaseClassInInlinedCodeID) { + + 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) = + i"""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(using Context) = + i"""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(using Context) = + i"""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(using Context) = i"${hl("@targetName")} annotation not allowed on top-level $symbol" + def explain(using Context) = + val annot = symbol.getAnnotation(defn.TargetNameAnnot).get + 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 + |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(using Context) = i"$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] + )(using Context) extends TypeMsg(MissingImplicitArgumentID), ShowMatchTrace(pt): + + arg.tpe match + case ambi: AmbiguousImplicits => withoutDisambiguation() + case _ => + + def msg(using Context): 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(i"${args(idx)}") else None + } - s"$kind is not a value" + """\$\{\s*([^}\s]+)\s*\}""".r.replaceAllIn(raw, (_: Regex.Match) match { + case Regex.Groups(v) => quoteReplacement(translate(v).getOrElse("")).nn + }) } - 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" - } - } - 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}" + /** 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 = + 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`. + */ + def userDefinedAmbiguousImplicitMsg(alt: SearchSuccess, raw: String) = { + val params = alt.ref.underlying match { + case p: PolyType => p.paramNames.map(_.toString) + case _ => Nil } - 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) { - em"""$clashDescription: - |${previousDecl.showDcl} ${symLocation(previousDecl)} and - |${decl.showDcl} ${symLocation(decl)} - |""" - } + details + 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) } - 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 = em"""$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 - } - - 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 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 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 - } - - class MemberWithSameNameAsStatic()(using Context) - extends SyntaxMsg(MemberWithSameNameAsStaticID) { - def msg = em"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 = - 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 - } - class TraitCompanionWithMutableStatic()(using Context) - extends SyntaxMsg(TraitCompanionWithMutableStaticID) { - def msg = em"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 explain = "" - } - - class StaticOverridingNonStaticMembers()(using Context) - extends SyntaxMsg(StaticOverridingNonStaticMembersID) { - def msg = em"${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 = - em"""The refinement `$rsym` introduces an overloaded definition. - |Refinements cannot contain overloaded definitions.""".stripMargin - } - - 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)}""" - def explain = "" - } - class StableIdentPattern(tree: untpd.Tree, pt: Type)(using Context) - extends TypeMsg(StableIdentPatternID) { - def msg = - em"""Stable identifier required, but $tree found""" - def explain = "" - } + /** @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) + ) + } - 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") + /** 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) } - 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 - |""".stripMargin } - 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}. - |""".stripMargin - } - - class UnknownNamedEnclosingClassOrObject(name: TypeName)(using Context) - extends ReferenceMsg(UnknownNamedEnclosingClassOrObjectID) { - def msg = - em"""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 + /** 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) + + object AmbiguousImplicitMsg { + def unapply(search: SearchSuccess): Option[String] = + userDefinedMsg(search.ref.symbol, defn.ImplicitAmbiguousAnnot) } - 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 = - em"""|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 - } - - class EnumerationsShouldNotBeEmpty(cdef: untpd.TypeDef)(using Context) - extends SyntaxMsg(EnumerationsShouldNotBeEmptyID) { - 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 - } - - 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 = - 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 - } - - class IllegalRedefinitionOfStandardKind(kindType: String, name: Name)(using Context) - extends SyntaxMsg(IllegalRedefinitionOfStandardKindID) { - def msg = em"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 - } - - class NoExtensionMethodAllowed(mdef: untpd.DefDef)(using Context) - extends SyntaxMsg(NoExtensionMethodAllowedID) { - def msg = em"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 - } - - 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 = - 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 - } - - class ExtensionCanOnlyHaveDefs(mdef: untpd.Tree)(using Context) - extends SyntaxMsg(ExtensionCanOnlyHaveDefsID) { - def msg = em"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 - } - - class UnexpectedPatternForSummonFrom(tree: Tree[_])(using Context) - extends SyntaxMsg(UnexpectedPatternForSummonFromID) { - def msg = em"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 - } - - 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 - } - - class ModifierNotAllowedForDefinition(flag: Flag)(using Context) - extends SyntaxMsg(ModifierNotAllowedForDefinitionID) { - def msg = em"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 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 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 = - em"""Case class/object definitions generate a considerable footprint in code size. - |Inlining such definition would multiply this footprint for each call site. - |""".stripMargin - } - - class ImplicitSearchTooLargeWarning(limit: Int, openSearchPairs: List[(Candidate, Type)])(using Context) - extends TypeMsg(ImplicitSearchTooLargeID): - override def showAlways = true - 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. - |""" - 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% - """ - - class TargetNameOnTopLevelClass(symbol: Symbol)(using Context) - extends SyntaxMsg(TargetNameOnTopLevelClassID): - def msg = em"${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")}""" + 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) => + i"""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)() + end msg + + 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(using Context) = + val pre = tpe.prefix + val name = tpe.name + val alts = tpe.denot.alternatives.map(_.symbol).filter(_.exists) + val whatCanNot = alts match + case Nil => + i"$name cannot" + case sym :: Nil => + i"${if (sym.owner == pre.typeSymbol) sym.show else sym.showLocated} cannot" + case _ => + 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)) + i"$whatCanNot be accessed as a member of $pre$where.$whyNot" + def explain(using Context) = "" - class NotClassType(tp: Type)(using Context) - extends TypeMsg(NotClassTypeID), ShowMatchTrace(tp): - def msg = ex"$tp is not a class type" - def explain = "" 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/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/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 c797c9fd92c1..557b2ac5a4c6 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -549,28 +549,30 @@ 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 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 { + 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) => @@ -780,7 +782,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/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 263b0040eb24..9e6c54b85304 100644 --- a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala @@ -246,13 +246,14 @@ 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, "") + .prepend(i"Reference to $tp within quotes requires a given $reqType in scope.\n") + .append("\n"), + 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/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/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/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)) } 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/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index dc8defa90eef..f11c8f5313dc 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -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/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 ec72c48b2422..29c104ccba0a 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 => @@ -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 @@ -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 => } } @@ -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}") @@ -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 99399832085f..4e35152667c2 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)) @@ -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 @@ -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 @@ -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 } @@ -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`. @@ -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 @@ -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 } @@ -935,8 +936,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. @@ -1022,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), "`") @@ -1049,14 +1050,14 @@ 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 } 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)) @@ -1111,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). @@ -1124,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, "") @@ -1174,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 _ => @@ -1188,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) } @@ -1231,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 { @@ -1348,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/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index b2e0a4481297..764d247280d4 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 => - em"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 => em" and expected result type $tp" + case tp => i" and expected result type $tp" } - em"(${tp.typedArgs().tpes}%, %)$result" + i"(${tp.typedArgs().tpes}%, %)$result" s"arguments ${argStr(tp)}" case _ => - em"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" } - em"$kind $tpe" + i"$kind $tpe" } def overloadedAltsStr(alts: List[SingleDenotation]): String = - em"overloaded alternatives of ${denotStr(alts.head)} with types\n" + - em" ${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) @@ -130,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 = @@ -263,195 +278,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) = - em"\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 0400d241e367..3e14292419a5 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -435,20 +435,15 @@ 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}" + i"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) i"match expected type" + else i"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) 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 - /** 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 +483,9 @@ object Implicits: map(tp) } - def explanation(using Context): String = + def msg(using Context): Message = em"no implicit values were found that $qualify" + override def toString = s"NoMatchingImplicits($expectedType, $argument)" } @@ -509,20 +505,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.append("\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 - 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 "" @@ -536,21 +532,21 @@ object Implicits: class MismatchedImplicit(ref: TermRef, val expectedType: Type, val argument: Tree) extends SearchFailureType { - def explanation(using Context): String = + 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 = + 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) = em"$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 +554,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 +565,7 @@ object Implicits: else reasons.mkString(" ", "", "") - def explanation(using Context) = em"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 @@ -851,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 _ => @@ -905,7 +900,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 +923,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) } /** A string indicating the formal parameter corresponding to a missing argument */ @@ -939,10 +933,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" + i"parameter of ${qual.tpe.widen}$addendum" case _ => - em"${ if paramName.is(EvidenceParamName) then "an implicit parameter" - else s"parameter $paramName" } of $methodStr" + i"${ if paramName.is(EvidenceParamName) then "an implicit parameter" + else s"parameter $paramName" } of $methodStr" } /** A CanEqual[T, U] instance is assumed @@ -1050,7 +1044,8 @@ 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 + .prepend(s"According to new implicit resolution rules, this will be ambiguous:\n"), ctx.source.atSpan(span)) altResult case _ => @@ -1357,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 6aab561c44b7..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 } @@ -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, i" in inferred superclass $first") :: parents end ensureFirstIsClass /** Add child annotation for `child` to annotations of `cls`. The annotation @@ -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 @@ -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.""", @@ -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 fa29f450be2a..2fe5770c5b4b 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 @@ -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 8afcec4dee63..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) } } @@ -319,20 +317,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 +335,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 @@ -396,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) = @@ -482,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)) @@ -539,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") @@ -562,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 = { @@ -816,7 +793,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 +824,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 +838,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 @@ -964,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 b90409e72364..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 @@ -167,26 +168,13 @@ 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) 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 => - em"$name cannot" - case sym :: Nil => - em"${if (sym.owner == pre.typeSymbol) sym.show else sym.showLocated} cannot" - case _ => - em"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(CannotBeAccessed(tpe, superAccess), 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..727433bdb21c 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 @@ -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 @@ -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) @@ -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 _ => @@ -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`. @@ -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 = { @@ -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 @@ -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) @@ -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: 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 + } 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/compiler/test-resources/repl/i4184 b/compiler/test-resources/repl/i4184 index 2c4eb7d12a6f..06b2c81ece21 100644 --- a/compiler/test-resources/repl/i4184 +++ b/compiler/test-resources/repl/i4184 @@ -5,8 +5,11 @@ 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 != -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 e745fa515443..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 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 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/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/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/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-custom-args/i13838.check b/tests/neg-custom-args/i13838.check index 2c93e4001461..5e62779f3238 100644 --- a/tests/neg-custom-args/i13838.check +++ b/tests/neg-custom-args/i13838.check @@ -1,15 +1,15 @@ --- 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 - | - |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-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/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]; 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/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/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/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/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/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..3f318d0b0111 100644 --- a/tests/neg/i9568.check +++ b/tests/neg/i9568.check @@ -1,12 +1,16 @@ --- 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 - | - | 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 + | . 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