Skip to content

Commit e25362d

Browse files
authored
Refactor string interpolators and error message handling (#16384)
The idea is that we want to concentrate error handling logic in the `Message` type instead of dispersing it over various string interpolators. - [x] Pass more messages instead of strings to `report` methods. - [x] Make this easier by having string interpolators that produce messages - [x] Use messages instead of strings elsewhere - [x] Move logic for limiting size of messages and filtering out nonsensical errors into `Message`. From the doc comment for `Message`: ## 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` in file `messages.scala` and use that instead. The advantage is that these messages have unique IDs that can be referenced elsewhere.
2 parents c9ace66 + d704ede commit e25362d

File tree

157 files changed

+3730
-3665
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

157 files changed

+3730
-3665
lines changed

compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import dotty.tools.dotc.transform.SymUtils._
2323
import dotty.tools.dotc.util.Spans._
2424
import dotty.tools.dotc.core.Contexts._
2525
import dotty.tools.dotc.core.Phases._
26+
import dotty.tools.dotc.core.Decorators.em
2627
import dotty.tools.dotc.report
2728

2829
/*
@@ -700,7 +701,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
700701
var elemKind = arr.elementType
701702
val argsSize = args.length
702703
if (argsSize > dims) {
703-
report.error(s"too many arguments for array constructor: found ${args.length} but array has only $dims dimension(s)", ctx.source.atSpan(app.span))
704+
report.error(em"too many arguments for array constructor: found ${args.length} but array has only $dims dimension(s)", ctx.source.atSpan(app.span))
704705
}
705706
if (argsSize < dims) {
706707
/* In one step:

compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
7979
outputDirectory
8080
} catch {
8181
case ex: Throwable =>
82-
report.error(s"Couldn't create file for class $cName\n${ex.getMessage}", ctx.source.atSpan(csym.span))
82+
report.error(em"Couldn't create file for class $cName\n${ex.getMessage}", ctx.source.atSpan(csym.span))
8383
null
8484
}
8585
}
@@ -422,7 +422,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
422422
emitAssocs(nestedVisitor, assocs, bcodeStore)(innerClasesStore)
423423

424424
case t =>
425-
report.error(ex"Annotation argument is not a constant", t.sourcePos)
425+
report.error(em"Annotation argument is not a constant", t.sourcePos)
426426
}
427427
}
428428

@@ -871,10 +871,11 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
871871
try body
872872
catch {
873873
case ex: Throwable =>
874-
report.error(i"""|compiler bug: created invalid generic signature for $sym in ${sym.denot.owner.showFullName}
875-
|signature: $sig
876-
|if this is reproducible, please report bug at https://github.com/lampepfl/dotty/issues
877-
""".trim, sym.sourcePos)
874+
report.error(
875+
em"""|compiler bug: created invalid generic signature for $sym in ${sym.denot.owner.showFullName}
876+
|signature: $sig
877+
|if this is reproducible, please report bug at https://github.com/lampepfl/dotty/issues
878+
""", sym.sourcePos)
878879
throw ex
879880
}
880881
}

compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -748,7 +748,7 @@ trait BCodeSkelBuilder extends BCodeHelpers {
748748

749749
if (params.size > MaximumJvmParameters) {
750750
// SI-7324
751-
report.error(s"Platform restriction: a parameter list's length cannot exceed $MaximumJvmParameters.", ctx.source.atSpan(methSymbol.span))
751+
report.error(em"Platform restriction: a parameter list's length cannot exceed $MaximumJvmParameters.", ctx.source.atSpan(methSymbol.span))
752752
return
753753
}
754754

@@ -800,9 +800,10 @@ trait BCodeSkelBuilder extends BCodeHelpers {
800800
val veryFirstProgramPoint = currProgramPoint()
801801

802802
if trimmedRhs == tpd.EmptyTree then
803-
report.error("Concrete method has no definition: " + dd + (
804-
if (ctx.settings.Ydebug.value) "(found: " + methSymbol.owner.info.decls.toList.mkString(", ") + ")"
805-
else ""),
803+
report.error(
804+
em"Concrete method has no definition: $dd${
805+
if (ctx.settings.Ydebug.value) "(found: " + methSymbol.owner.info.decls.toList.mkString(", ") + ")"
806+
else ""}",
806807
ctx.source.atSpan(NoSpan)
807808
)
808809
else

compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import Contexts._
1414
import Types._
1515
import Symbols._
1616
import Phases._
17+
import Decorators.em
1718

1819
import dotty.tools.dotc.util.ReadOnlyMap
1920
import dotty.tools.dotc.report
@@ -71,7 +72,7 @@ class DottyBackendInterface(val outputDirectory: AbstractFile, val superCallsMap
7172
def _1: Type = field.tpe match {
7273
case JavaArrayType(elem) => elem
7374
case _ =>
74-
report.error(s"JavaSeqArray with type ${field.tpe} reached backend: $field", ctx.source.atSpan(field.span))
75+
report.error(em"JavaSeqArray with type ${field.tpe} reached backend: $field", ctx.source.atSpan(field.span))
7576
UnspecifiedErrorType
7677
}
7778
def _2: List[Tree] = field.elems

compiler/src/dotty/tools/backend/jvm/GenBCode.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import dotty.tools.dotc.sbt.ExtractDependencies
2121
import Contexts._
2222
import Phases._
2323
import Symbols._
24+
import Decorators.em
2425

2526
import java.io.DataOutputStream
2627
import java.nio.channels.ClosedByInterruptException
@@ -308,7 +309,7 @@ class GenBCodePipeline(val int: DottyBackendInterface, val primitives: DottyPrim
308309
getFileForClassfile(outF, cls.name, ".class")
309310
} catch {
310311
case e: FileConflictException =>
311-
report.error(s"error writing ${cls.name}: ${e.getMessage}")
312+
report.error(em"error writing ${cls.name}: ${e.getMessage}")
312313
null
313314
}
314315
} else null
@@ -608,11 +609,11 @@ class GenBCodePipeline(val int: DottyBackendInterface, val primitives: DottyPrim
608609
val method =
609610
s"${e.getClassName.replaceAll("/", ".")}.${e.getMethodName}"
610611
val msg =
611-
s"Generated bytecode for method '$method' is too large. Size: ${e.getCodeSize} bytes. Limit is 64KB"
612+
em"Generated bytecode for method '$method' is too large. Size: ${e.getCodeSize} bytes. Limit is 64KB"
612613
report.error(msg)
613614
case e: ClassTooLargeException =>
614615
val msg =
615-
s"Class '${e.getClassName.replaceAll("/", ".")}' is too large. Constant pool size: ${e.getConstantPoolCount}. Limit is 64K entries"
616+
em"Class '${e.getClassName.replaceAll("/", ".")}' is too large. Constant pool size: ${e.getConstantPoolCount}. Limit is 64K entries"
616617
report.error(msg)
617618

618619
}

compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Contexts._
88
import Names.TermName, StdNames._
99
import Types.{JavaArrayType, UnspecifiedErrorType, Type}
1010
import Symbols.{Symbol, NoSymbol}
11+
import Decorators.em
1112
import dotc.report
1213
import dotc.util.ReadOnlyMap
1314

@@ -66,7 +67,7 @@ class DottyPrimitives(ictx: Context) {
6667
case defn.ArrayOf(el) => el
6768
case JavaArrayType(el) => el
6869
case _ =>
69-
report.error(s"expected Array $tpe")
70+
report.error(em"expected Array $tpe")
7071
UnspecifiedErrorType
7172
}
7273

@@ -133,7 +134,7 @@ class DottyPrimitives(ictx: Context) {
133134
def addPrimitives(cls: Symbol, method: TermName, code: Int)(using Context): Unit = {
134135
val alts = cls.info.member(method).alternatives.map(_.symbol)
135136
if (alts.isEmpty)
136-
report.error(s"Unknown primitive method $cls.$method")
137+
report.error(em"Unknown primitive method $cls.$method")
137138
else alts foreach (s =>
138139
addPrimitive(s,
139140
s.info.paramInfoss match {

compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2926,7 +2926,7 @@ class JSCodeGen()(using genCtx: Context) {
29262926
case defn.ArrayOf(el) => el
29272927
case JavaArrayType(el) => el
29282928
case tpe =>
2929-
val msg = ex"expected Array $tpe"
2929+
val msg = em"expected Array $tpe"
29302930
report.error(msg)
29312931
ErrorType(msg)
29322932
}
@@ -3652,7 +3652,7 @@ class JSCodeGen()(using genCtx: Context) {
36523652
} else if (sym.isJSType) {
36533653
if (sym.is(Trait)) {
36543654
report.error(
3655-
s"isInstanceOf[${sym.fullName}] not supported because it is a JS trait",
3655+
em"isInstanceOf[${sym.fullName}] not supported because it is a JS trait",
36563656
pos)
36573657
js.BooleanLiteral(true)
36583658
} else {

compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) {
135135

136136
for ((info, _) <- tups.tail) {
137137
report.error(
138-
em"export overload conflicts with export of $firstSym: " +
139-
"a field may not share its exported name with another export",
138+
em"export overload conflicts with export of $firstSym: a field may not share its exported name with another export",
140139
info.pos)
141140
}
142141

@@ -264,8 +263,8 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) {
264263
.alternatives
265264

266265
assert(!alts.isEmpty,
267-
em"Ended up with no alternatives for ${classSym.fullName}::$name. " +
268-
em"Original set was ${alts} with types ${alts.map(_.info)}")
266+
em"""Ended up with no alternatives for ${classSym.fullName}::$name.
267+
|Original set was ${alts} with types ${alts.map(_.info)}""")
269268

270269
val (jsName, isProp) = exportNameInfo(name)
271270

@@ -309,7 +308,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) {
309308
if (isProp && methodSyms.nonEmpty) {
310309
val firstAlt = alts.head
311310
report.error(
312-
i"Conflicting properties and methods for ${classSym.fullName}::$name.",
311+
em"Conflicting properties and methods for ${classSym.fullName}::$name.",
313312
firstAlt.srcPos)
314313
implicit val pos = firstAlt.span
315314
js.JSPropertyDef(js.MemberFlags.empty, genExpr(name)(firstAlt.sourcePos), None, None)
@@ -613,7 +612,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) {
613612
val altsTypesInfo = alts.map(_.info.show).sorted.mkString("\n ")
614613

615614
report.error(
616-
s"Cannot disambiguate overloads for $fullKind $displayName with types\n $altsTypesInfo",
615+
em"Cannot disambiguate overloads for $fullKind $displayName with types\n $altsTypesInfo",
617616
pos)
618617
}
619618

compiler/src/dotty/tools/backend/sjs/JSPositions.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import java.net.{URI, URISyntaxException}
66

77
import dotty.tools.dotc.core._
88
import Contexts._
9+
import Decorators.em
910

1011
import dotty.tools.dotc.report
1112

@@ -31,7 +32,7 @@ class JSPositions()(using Context) {
3132
URIMap(from, to) :: Nil
3233
} catch {
3334
case e: URISyntaxException =>
34-
report.error(s"${e.getInput} is not a valid URI")
35+
report.error(em"${e.getInput} is not a valid URI")
3536
Nil
3637
}
3738
}

compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Names.TermName
55
import Types._
66
import Contexts._
77
import Symbols._
8+
import Decorators.em
89

910
import dotty.tools.dotc.ast.tpd._
1011
import dotty.tools.backend.jvm.DottyPrimitives
@@ -90,7 +91,7 @@ class JSPrimitives(ictx: Context) extends DottyPrimitives(ictx) {
9091
def addPrimitives(cls: Symbol, method: TermName, code: Int)(using Context): Unit = {
9192
val alts = cls.info.member(method).alternatives.map(_.symbol)
9293
if (alts.isEmpty) {
93-
report.error(s"Unknown primitive method $cls.$method")
94+
report.error(em"Unknown primitive method $cls.$method")
9495
} else {
9596
for (s <- alts)
9697
addPrimitive(s, code)

compiler/src/dotty/tools/dotc/CompilationUnit.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,11 @@ object CompilationUnit {
131131
if (!mustExist)
132132
source
133133
else if (source.file.isDirectory) {
134-
report.error(s"expected file, received directory '${source.file.path}'")
134+
report.error(em"expected file, received directory '${source.file.path}'")
135135
NoSource
136136
}
137137
else if (!source.file.exists) {
138-
report.error(s"source file not found: ${source.file.path}")
138+
report.error(em"source file not found: ${source.file.path}")
139139
NoSource
140140
}
141141
else source

compiler/src/dotty/tools/dotc/Driver.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,18 +94,18 @@ class Driver {
9494
val newEntries: List[String] = files
9595
.flatMap { file =>
9696
if !file.exists then
97-
report.error(s"File does not exist: ${file.path}")
97+
report.error(em"File does not exist: ${file.path}")
9898
None
9999
else file.extension match
100100
case "jar" => Some(file.path)
101101
case "tasty" =>
102102
TastyFileUtil.getClassPath(file) match
103103
case Some(classpath) => Some(classpath)
104104
case _ =>
105-
report.error(s"Could not load classname from: ${file.path}")
105+
report.error(em"Could not load classname from: ${file.path}")
106106
None
107107
case _ =>
108-
report.error(s"File extension is not `tasty` or `jar`: ${file.path}")
108+
report.error(em"File extension is not `tasty` or `jar`: ${file.path}")
109109
None
110110
}
111111
.distinct

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ object desugar {
118118
if (local.exists) (defctx.owner.thisType select local).dealiasKeepAnnots
119119
else {
120120
def msg =
121-
s"no matching symbol for ${tp.symbol.showLocated} in ${defctx.owner} / ${defctx.effectiveScope.toList}"
121+
em"no matching symbol for ${tp.symbol.showLocated} in ${defctx.owner} / ${defctx.effectiveScope.toList}"
122122
ErrorType(msg).assertingErrorsReported(msg)
123123
}
124124
case _ =>
@@ -921,7 +921,7 @@ object desugar {
921921
case params :: paramss1 => // `params` must have a single parameter and without `given` flag
922922

923923
def badRightAssoc(problem: String) =
924-
report.error(i"right-associative extension method $problem", mdef.srcPos)
924+
report.error(em"right-associative extension method $problem", mdef.srcPos)
925925
extParamss ++ mdef.paramss
926926

927927
params match
@@ -1147,7 +1147,7 @@ object desugar {
11471147
def errorOnGivenBinding(bind: Bind)(using Context): Boolean =
11481148
report.error(
11491149
em"""${hl("given")} patterns are not allowed in a ${hl("val")} definition,
1150-
|please bind to an identifier and use an alias given.""".stripMargin, bind)
1150+
|please bind to an identifier and use an alias given.""", bind)
11511151
false
11521152

11531153
def isTuplePattern(arity: Int): Boolean = pat match {
@@ -1247,7 +1247,7 @@ object desugar {
12471247
def checkOpaqueAlias(tree: MemberDef)(using Context): MemberDef =
12481248
def check(rhs: Tree): MemberDef = rhs match
12491249
case bounds: TypeBoundsTree if bounds.alias.isEmpty =>
1250-
report.error(i"opaque type must have a right-hand side", tree.srcPos)
1250+
report.error(em"opaque type must have a right-hand side", tree.srcPos)
12511251
tree.withMods(tree.mods.withoutFlags(Opaque))
12521252
case LambdaTypeTree(_, body) => check(body)
12531253
case _ => tree

compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ object DesugarEnums {
216216
case Ident(name) =>
217217
val matches = tparamNames.contains(name)
218218
if (matches && (caseTypeParams.nonEmpty || vparamss.isEmpty))
219-
report.error(i"illegal reference to type parameter $name from enum case", tree.srcPos)
219+
report.error(em"illegal reference to type parameter $name from enum case", tree.srcPos)
220220
matches
221221
case LambdaTypeTree(lambdaParams, body) =>
222222
underBinders(lambdaParams, foldOver(x, tree))

compiler/src/dotty/tools/dotc/ast/MainProxies.scala

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ object MainProxies {
5656

5757
def addArgs(call: untpd.Tree, mt: MethodType, idx: Int): untpd.Tree =
5858
if (mt.isImplicitMethod) {
59-
report.error(s"@main method cannot have implicit parameters", pos)
59+
report.error(em"@main method cannot have implicit parameters", pos)
6060
call
6161
}
6262
else {
@@ -74,7 +74,7 @@ object MainProxies {
7474
mt.resType match {
7575
case restpe: MethodType =>
7676
if (mt.paramInfos.lastOption.getOrElse(NoType).isRepeatedParam)
77-
report.error(s"varargs parameter of @main method must come last", pos)
77+
report.error(em"varargs parameter of @main method must come last", pos)
7878
addArgs(call1, restpe, idx + args.length)
7979
case _ =>
8080
call1
@@ -83,17 +83,17 @@ object MainProxies {
8383

8484
var result: List[TypeDef] = Nil
8585
if (!mainFun.owner.isStaticOwner)
86-
report.error(s"@main method is not statically accessible", pos)
86+
report.error(em"@main method is not statically accessible", pos)
8787
else {
8888
var call = ref(mainFun.termRef)
8989
mainFun.info match {
9090
case _: ExprType =>
9191
case mt: MethodType =>
9292
call = addArgs(call, mt, 0)
9393
case _: PolyType =>
94-
report.error(s"@main method cannot have type parameters", pos)
94+
report.error(em"@main method cannot have type parameters", pos)
9595
case _ =>
96-
report.error(s"@main can only annotate a method", pos)
96+
report.error(em"@main can only annotate a method", pos)
9797
}
9898
val errVar = Ident(nme.error)
9999
val handler = CaseDef(
@@ -203,7 +203,7 @@ object MainProxies {
203203
))
204204
(sym, paramAnnotations.toVector, defaultValueSymbols(scope, sym), stat.rawComment) :: Nil
205205
case mainAnnot :: others =>
206-
report.error(s"method cannot have multiple main annotations", mainAnnot.tree)
206+
report.error(em"method cannot have multiple main annotations", mainAnnot.tree)
207207
Nil
208208
}
209209
case stat @ TypeDef(_, impl: Template) if stat.symbol.is(Module) =>
@@ -379,26 +379,26 @@ object MainProxies {
379379
end generateMainClass
380380

381381
if (!mainFun.owner.isStaticOwner)
382-
report.error(s"main method is not statically accessible", pos)
382+
report.error(em"main method is not statically accessible", pos)
383383
None
384384
else mainFun.info match {
385385
case _: ExprType =>
386386
Some(generateMainClass(unitToValue(ref(mainFun.termRef)), Nil, Nil))
387387
case mt: MethodType =>
388388
if (mt.isImplicitMethod)
389-
report.error(s"main method cannot have implicit parameters", pos)
389+
report.error(em"main method cannot have implicit parameters", pos)
390390
None
391391
else mt.resType match
392392
case restpe: MethodType =>
393-
report.error(s"main method cannot be curried", pos)
393+
report.error(em"main method cannot be curried", pos)
394394
None
395395
case _ =>
396396
Some(generateMainClass(unitToValue(Apply(ref(mainFun.termRef), argRefs(mt))), argValDefs(mt), parameterInfos(mt)))
397397
case _: PolyType =>
398-
report.error(s"main method cannot have type parameters", pos)
398+
report.error(em"main method cannot have type parameters", pos)
399399
None
400400
case _ =>
401-
report.error(s"main can only annotate a method", pos)
401+
report.error(em"main can only annotate a method", pos)
402402
None
403403
}
404404
}

0 commit comments

Comments
 (0)