Skip to content

Commit 94e9ba1

Browse files
committed
Turn more strings into messages
In particular: The missingArg message of ImplicitSearchError becomes a regular Message now.
1 parent 77dbd3b commit 94e9ba1

Some content is hidden

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

62 files changed

+442
-434
lines changed

compiler/src/dotty/tools/dotc/inlines/Inlines.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -442,8 +442,7 @@ object Inlines:
442442
val evidence = evTyper.inferImplicitArg(tpt.tpe, tpt.span)
443443
evidence.tpe match
444444
case fail: Implicits.SearchFailureType =>
445-
val msg = evTyper.missingArgMsg(evidence, tpt.tpe, "")
446-
errorTree(call, em"$msg")
445+
errorTree(call, evTyper.missingArgMsg(evidence, tpt.tpe, ""))
447446
case _ =>
448447
evidence
449448
}

compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
185185
case TargetNameOnTopLevelClassID // errorNumber: 169
186186
case NotClassTypeID // errorNumber 170
187187
case MissingArgumentID // errorNumer 171
188+
case MissingImplicitArgumentID // errorNumber 172
189+
case CannotBeAccessedID // errorNumber 173
188190

189191
def errorNumber = ordinal - 1
190192

compiler/src/dotty/tools/dotc/reporting/Message.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ abstract class Message(val errorId: ErrorMessageID)(using Context) { self =>
133133
def explain = self.explain ++ suffix
134134
override def canExplain = true
135135

136+
def wrap(header: => String, trailer: => String): Message =
137+
mapMsg(s => header ++ s ++ trailer)
138+
136139
/** Override with `true` for messages that should always be shown even if their
137140
* position overlaps another messsage of a different class. On the other hand
138141
* multiple messages of the same class with overlapping positions will lead
@@ -144,7 +147,7 @@ abstract class Message(val errorId: ErrorMessageID)(using Context) { self =>
144147
}
145148

146149
/** The fallback `Message` containing no explanation and having no `kind` */
147-
class NoExplanation(msgFn: => String)(using Context) extends Message(ErrorMessageID.NoExplanationID) {
150+
final class NoExplanation(msgFn: => String)(using Context) extends Message(ErrorMessageID.NoExplanationID) {
148151
def msg: String = msgFn
149152
def explain: String = ""
150153
val kind: MessageKind = MessageKind.NoKind

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

Lines changed: 268 additions & 56 deletions
Large diffs are not rendered by default.

compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,10 +246,10 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(
246246
checkStable(tp, pos, "type witness")
247247
getQuoteTypeTags.getTagRef(tp)
248248
case _: SearchFailureType =>
249-
report.error(i"""Reference to $tp within quotes requires a given $reqType in scope.
250-
|${ctx.typer.missingArgMsg(tag, reqType, "")}
251-
|
252-
|""", pos)
249+
report.error(
250+
ctx.typer.missingArgMsg(tag, reqType, "").wrap(
251+
i"Reference to $tp within quotes requires a given $reqType in scope.\n", "\n"),
252+
pos)
253253
tp
254254
case _ =>
255255
report.error(i"""Reference to $tp within quotes requires a given $reqType in scope.

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ trait Applications extends Compatibility {
478478
matchArgs(orderedArgs, methType.paramInfos, 0)
479479
case _ =>
480480
if (methType.isError) ok = false
481-
else fail(s"$methString does not take parameters".toMessage)
481+
else fail(em"$methString does not take parameters")
482482
}
483483

484484
/** The application was successful */
@@ -518,10 +518,10 @@ trait Applications extends Compatibility {
518518
else { // name not (or no longer) available for named arg
519519
def msg =
520520
if (methodType.paramNames contains aname)
521-
s"parameter $aname of $methString is already instantiated"
521+
em"parameter $aname of $methString is already instantiated"
522522
else
523-
s"$methString does not have a parameter $aname"
524-
fail(msg.toMessage, arg.asInstanceOf[Arg])
523+
em"$methString does not have a parameter $aname"
524+
fail(msg, arg.asInstanceOf[Arg])
525525
arg :: handleNamed(pnamesRest, args1, nameToArg, toDrop)
526526
}
527527
case arg :: args1 =>
@@ -647,10 +647,10 @@ trait Applications extends Compatibility {
647647
def msg = arg match
648648
case untpd.Tuple(Nil)
649649
if applyKind == ApplyKind.InfixTuple && funType.widen.isNullaryMethod =>
650-
i"can't supply unit value with infix notation because nullary $methString takes no arguments; use dotted invocation instead: (...).${methRef.name}()"
650+
em"can't supply unit value with infix notation because nullary $methString takes no arguments; use dotted invocation instead: (...).${methRef.name}()"
651651
case _ =>
652-
i"too many arguments for $methString"
653-
fail(msg.toMessage, arg)
652+
em"too many arguments for $methString"
653+
fail(msg, arg)
654654
case nil =>
655655
}
656656
}

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -843,9 +843,9 @@ trait Checking {
843843
case Select(id, _) => id
844844
case _ => EmptyTree
845845
if extractor.isEmpty then
846-
e"pattern binding uses refutable extractor"
846+
i"pattern binding uses refutable extractor"
847847
else
848-
e"pattern binding uses refutable extractor `$extractor`"
848+
i"pattern binding uses refutable extractor `$extractor`"
849849

850850
val fix =
851851
if isPatDef then "adding `: @unchecked` after the expression"

compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala

Lines changed: 7 additions & 201 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,9 @@ import Trees._
1010
import NameOps._
1111
import util.SrcPos
1212
import config.Feature
13-
import java.util.regex.Matcher.quoteReplacement
1413
import reporting._
1514
import collection.mutable
1615

17-
import scala.util.matching.Regex
1816

1917
object ErrorReporting {
2018

@@ -87,18 +85,18 @@ object ErrorReporting {
8785

8886
def expectedTypeStr(tp: Type): String = tp match {
8987
case tp: PolyProto =>
90-
e"type arguments [${tp.targs.tpes}%, %] and ${expectedTypeStr(revealDeepenedArgs(tp.resultType))}"
88+
i"type arguments [${tp.targs.tpes}%, %] and ${expectedTypeStr(revealDeepenedArgs(tp.resultType))}"
9189
case tp: FunProto =>
9290
def argStr(tp: FunProto): String =
9391
val result = revealDeepenedArgs(tp.resultType) match {
9492
case restp: FunProto => argStr(restp)
9593
case _: WildcardType | _: IgnoredProto => ""
96-
case tp => e" and expected result type $tp"
94+
case tp => i" and expected result type $tp"
9795
}
98-
e"(${tp.typedArgs().tpes}%, %)$result"
96+
i"(${tp.typedArgs().tpes}%, %)$result"
9997
s"arguments ${argStr(tp)}"
10098
case _ =>
101-
e"expected type $tp"
99+
i"expected type $tp"
102100
}
103101

104102
def anonymousTypeMemberStr(tpe: Type): String = {
@@ -107,12 +105,12 @@ object ErrorReporting {
107105
case _: MethodOrPoly => "method"
108106
case _ => "value of type"
109107
}
110-
e"$kind $tpe"
108+
i"$kind $tpe"
111109
}
112110

113111
def overloadedAltsStr(alts: List[SingleDenotation]): String =
114-
e"overloaded alternatives of ${denotStr(alts.head)} with types\n" +
115-
e" ${alts map (_.info)}%\n %"
112+
i"""overloaded alternatives of ${denotStr(alts.head)} with types
113+
| ${alts map (_.info)}%\n %"""
116114

117115
def denotStr(denot: Denotation): String =
118116
if (denot.isOverloaded) overloadedAltsStr(denot.alternatives)
@@ -263,195 +261,3 @@ object ErrorReporting {
263261

264262
def err(using Context): Errors = new Errors
265263
}
266-
267-
class ImplicitSearchError(
268-
arg: tpd.Tree,
269-
pt: Type,
270-
where: String,
271-
paramSymWithMethodCallTree: Option[(Symbol, tpd.Tree)] = None,
272-
ignoredInstanceNormalImport: => Option[SearchSuccess],
273-
importSuggestionAddendum: => String
274-
)(using ctx: Context) {
275-
276-
def missingArgMsg = arg.tpe match {
277-
case ambi: AmbiguousImplicits =>
278-
(ambi.alt1, ambi.alt2) match {
279-
case (alt @ AmbiguousImplicitMsg(msg), _) =>
280-
userDefinedAmbiguousImplicitMsg(alt, msg)
281-
case (_, alt @ AmbiguousImplicitMsg(msg)) =>
282-
userDefinedAmbiguousImplicitMsg(alt, msg)
283-
case _ =>
284-
defaultAmbiguousImplicitMsg(ambi)
285-
}
286-
case ambi @ TooUnspecific(target) =>
287-
ex"""No implicit search was attempted${location("for")}
288-
|since the expected type $target is not specific enough"""
289-
case _ =>
290-
val shortMessage = userDefinedImplicitNotFoundParamMessage
291-
.orElse(userDefinedImplicitNotFoundTypeMessage)
292-
.getOrElse(defaultImplicitNotFoundMessage)
293-
formatMsg(shortMessage)()
294-
++ hiddenImplicitsAddendum
295-
++ ErrorReporting.matchReductionAddendum(pt)
296-
}
297-
298-
private def formatMsg(shortForm: String)(headline: String = shortForm) = arg match
299-
case arg: Trees.SearchFailureIdent[?] =>
300-
arg.tpe match
301-
case _: NoMatchingImplicits => headline
302-
case tpe: SearchFailureType =>
303-
i"$headline. ${tpe.explanation}"
304-
case _ => headline
305-
case _ =>
306-
arg.tpe match
307-
case tpe: SearchFailureType =>
308-
val original = arg match
309-
case Inlined(call, _, _) => call
310-
case _ => arg
311-
i"""$headline.
312-
|I found:
313-
|
314-
| ${original.show.replace("\n", "\n ")}
315-
|
316-
|But ${tpe.explanation}."""
317-
case _ => headline
318-
319-
/** Format `raw` implicitNotFound or implicitAmbiguous argument, replacing
320-
* all occurrences of `${X}` where `X` is in `paramNames` with the
321-
* corresponding shown type in `args`.
322-
*/
323-
private def userDefinedErrorString(raw: String, paramNames: List[String], args: List[Type]): String = {
324-
def translate(name: String): Option[String] = {
325-
val idx = paramNames.indexOf(name)
326-
if (idx >= 0) Some(ex"${args(idx)}") else None
327-
}
328-
329-
"""\$\{\s*([^}\s]+)\s*\}""".r.replaceAllIn(raw, (_: Regex.Match) match {
330-
case Regex.Groups(v) => quoteReplacement(translate(v).getOrElse("")).nn
331-
})
332-
}
333-
334-
/** Extract a user defined error message from a symbol `sym`
335-
* with an annotation matching the given class symbol `cls`.
336-
*/
337-
private def userDefinedMsg(sym: Symbol, cls: Symbol) = for {
338-
ann <- sym.getAnnotation(cls)
339-
msg <- ann.argumentConstantString(0)
340-
} yield msg
341-
342-
private def location(preposition: String) = if (where.isEmpty) "" else s" $preposition $where"
343-
344-
private def defaultAmbiguousImplicitMsg(ambi: AmbiguousImplicits) =
345-
s"Ambiguous given instances: ${ambi.explanation}${location("of")}"
346-
347-
private def defaultImplicitNotFoundMessage =
348-
ex"No given instance of type $pt was found${location("for")}"
349-
350-
/** Construct a custom error message given an ambiguous implicit
351-
* candidate `alt` and a user defined message `raw`.
352-
*/
353-
private def userDefinedAmbiguousImplicitMsg(alt: SearchSuccess, raw: String) = {
354-
val params = alt.ref.underlying match {
355-
case p: PolyType => p.paramNames.map(_.toString)
356-
case _ => Nil
357-
}
358-
def resolveTypes(targs: List[tpd.Tree])(using Context) =
359-
targs.map(a => Inferencing.fullyDefinedType(a.tpe, "type argument", a.srcPos))
360-
361-
// We can extract type arguments from:
362-
// - a function call:
363-
// @implicitAmbiguous("msg A=${A}")
364-
// implicit def f[A](): String = ...
365-
// implicitly[String] // found: f[Any]()
366-
//
367-
// - an eta-expanded function:
368-
// @implicitAmbiguous("msg A=${A}")
369-
// implicit def f[A](x: Int): String = ...
370-
// implicitly[Int => String] // found: x => f[Any](x)
371-
372-
val call = tpd.closureBody(alt.tree) // the tree itself if not a closure
373-
val targs = tpd.typeArgss(call).flatten
374-
val args = resolveTypes(targs)(using ctx.fresh.setTyperState(alt.tstate))
375-
userDefinedErrorString(raw, params, args)
376-
}
377-
378-
/** @param rawMsg Message template with variables, e.g. "Variable A is ${A}"
379-
* @param sym Symbol of the annotated type or of the method whose parameter was annotated
380-
* @param substituteType Function substituting specific types for abstract types associated with variables, e.g A -> Int
381-
*/
382-
private def formatAnnotationMessage(rawMsg: String, sym: Symbol, substituteType: Type => Type): String = {
383-
val substitutableTypesSymbols = ErrorReporting.substitutableTypeSymbolsInScope(sym)
384-
385-
userDefinedErrorString(
386-
rawMsg,
387-
paramNames = substitutableTypesSymbols.map(_.name.unexpandedName.toString),
388-
args = substitutableTypesSymbols.map(_.typeRef).map(substituteType)
389-
)
390-
}
391-
392-
/** Extracting the message from a method parameter, e.g. in
393-
*
394-
* trait Foo
395-
*
396-
* def foo(implicit @annotation.implicitNotFound("Foo is missing") foo: Foo): Any = ???
397-
*/
398-
private def userDefinedImplicitNotFoundParamMessage: Option[String] = paramSymWithMethodCallTree.flatMap { (sym, applTree) =>
399-
userDefinedMsg(sym, defn.ImplicitNotFoundAnnot).map { rawMsg =>
400-
val fn = tpd.funPart(applTree)
401-
val targs = tpd.typeArgss(applTree).flatten
402-
val methodOwner = fn.symbol.owner
403-
val methodOwnerType = tpd.qualifier(fn).tpe
404-
val methodTypeParams = fn.symbol.paramSymss.flatten.filter(_.isType)
405-
val methodTypeArgs = targs.map(_.tpe)
406-
val substituteType = (_: Type).asSeenFrom(methodOwnerType, methodOwner).subst(methodTypeParams, methodTypeArgs)
407-
formatAnnotationMessage(rawMsg, sym.owner, substituteType)
408-
}
409-
}
410-
411-
/** Extracting the message from a type, e.g. in
412-
*
413-
* @annotation.implicitNotFound("Foo is missing")
414-
* trait Foo
415-
*
416-
* def foo(implicit foo: Foo): Any = ???
417-
*/
418-
private def userDefinedImplicitNotFoundTypeMessage: Option[String] =
419-
def recur(tp: Type): Option[String] = tp match
420-
case tp: TypeRef =>
421-
val sym = tp.symbol
422-
userDefinedImplicitNotFoundTypeMessage(sym).orElse(recur(tp.info))
423-
case tp: ClassInfo =>
424-
tp.baseClasses.iterator
425-
.map(userDefinedImplicitNotFoundTypeMessage)
426-
.find(_.isDefined).flatten
427-
case tp: TypeProxy =>
428-
recur(tp.superType)
429-
case tp: AndType =>
430-
recur(tp.tp1).orElse(recur(tp.tp2))
431-
case _ =>
432-
None
433-
recur(pt)
434-
435-
private def userDefinedImplicitNotFoundTypeMessage(sym: Symbol): Option[String] =
436-
for
437-
rawMsg <- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot)
438-
if Feature.migrateTo3 || sym != defn.Function1
439-
// Don't inherit "No implicit view available..." message if subtypes of Function1 are not treated as implicit conversions anymore
440-
yield
441-
val substituteType = (_: Type).asSeenFrom(pt, sym)
442-
formatAnnotationMessage(rawMsg, sym, substituteType)
443-
444-
private def hiddenImplicitsAddendum: String =
445-
def hiddenImplicitNote(s: SearchSuccess) =
446-
e"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`."
447-
448-
val normalImports = ignoredInstanceNormalImport.map(hiddenImplicitNote)
449-
450-
normalImports.getOrElse(importSuggestionAddendum)
451-
end hiddenImplicitsAddendum
452-
453-
private object AmbiguousImplicitMsg {
454-
def unapply(search: SearchSuccess): Option[String] =
455-
userDefinedMsg(search.ref.symbol, defn.ImplicitAmbiguousAnnot)
456-
}
457-
}

0 commit comments

Comments
 (0)