diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala
index 1fdb513a2f55..e6943bca6ce3 100644
--- a/compiler/src/dotty/tools/dotc/reporting/messages.scala
+++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala
@@ -2447,8 +2447,8 @@ import transform.SymUtils._
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 in the signature of ${hl(owner)}.
+ 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 = ""
}
diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala
index 61ccd2d9f530..d25e095b3325 100644
--- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala
+++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala
@@ -6,12 +6,16 @@ import ast._
import core._
import Types._, ProtoTypes._, Contexts._, Decorators._, Denotations._, Symbols._
import Implicits._, Flags._, Constants.Constant
+import Trees._
+import NameOps._
import util.Spans._
import util.SrcPos
import config.Feature
import java.util.regex.Matcher.quoteReplacement
import reporting._
+import scala.util.matching.Regex
+
object ErrorReporting {
import tpd._
@@ -131,14 +135,6 @@ object ErrorReporting {
* 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] = {
- assert(paramNames.length == args.length)
- val idx = paramNames.indexOf(name)
- if (idx >= 0) Some(quoteReplacement(ex"${args(idx)}")) else None
- }
- """\$\{\w*\}""".r.replaceSomeIn(raw, m => translate(m.matched.drop(2).init))
- }
def rewriteNotice: String =
if Feature.migrateTo3 then "\nThis patch can be inserted automatically under -rewrite."
@@ -180,9 +176,180 @@ object ErrorReporting {
end selectErrorAddendum
}
+ def substitutableTypeSymbolsInScope(sym: Symbol)(using Context): List[Symbol] =
+ sym.ownersIterator.takeWhile(!_.is(Flags.Package)).flatMap { ownerSym =>
+ ownerSym.paramSymss.flatten.filter(_.isType) ++
+ ownerSym.typeRef.nonClassTypeMembers.map(_.symbol)
+ }.toList
+
def dependentStr =
"""Term-dependent types are experimental,
|they must be enabled with a `experimental.dependent` language import or setting""".stripMargin
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 _ =>
+ val shortMessage = userDefinedImplicitNotFoundParamMessage
+ .orElse(userDefinedImplicitNotFoundTypeMessage)
+ .getOrElse(defaultImplicitNotFoundMessage)
+ formatMsg(shortMessage)() ++ hiddenImplicitsAddendum
+ }
+
+ private def formatMsg(shortForm: String)(headline: String = shortForm) = arg match {
+ case arg: Trees.SearchFailureIdent[?] =>
+ shortForm
+ 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}."""
+ }
+ }
+
+ 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(""))
+ })
+ }
+
+ /** 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)
+ Trees.Literal(Constant(msg: String)) <- ann.argument(0)
+ } yield msg
+
+ private def location(preposition: String) = if (where.isEmpty) "" else s" $preposition $where"
+
+ private def defaultAmbiguousImplicitMsg(ambi: AmbiguousImplicits) = {
+ formatMsg(s"ambiguous implicit arguments: ${ambi.explanation}${location("of")}")(
+ s"ambiguous implicit arguments of type ${pt.show} found${location("for")}"
+ )
+ }
+
+ private def defaultImplicitNotFoundMessage = {
+ em"no implicit argument 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.span))
+
+ // 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.decomposeCall(call)
+ 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 = paramSymWithMethodCallTree.flatMap { (sym, applTree) =>
+ userDefinedMsg(sym, defn.ImplicitNotFoundAnnot).map { rawMsg =>
+ val (fn, targs, _) = tpd.decomposeCall(applTree)
+ 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 =
+ val classSym = pt.classSymbol
+ userDefinedMsg(classSym, defn.ImplicitNotFoundAnnot).map { rawMsg =>
+ val substituteType = (_: Type).asSeenFrom(pt, classSym)
+ formatAnnotationMessage(rawMsg, classSym, substituteType)
+ }
+
+ private def hiddenImplicitsAddendum: String =
+ def hiddenImplicitNote(s: SearchSuccess) =
+ em"\n\nNote: given instance ${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 6e7185d73bfb..c97c41098182 100644
--- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala
@@ -82,6 +82,7 @@ object Implicits:
def strictEquality(using Context): Boolean =
ctx.mode.is(Mode.StrictEquality) || Feature.enabled(nme.strictEquality)
+
/** A common base class of contextual implicits and of-type implicits which
* represents a set of references to implicit definitions.
*/
@@ -832,126 +833,40 @@ trait Implicits:
arg
}
- def missingArgMsg(arg: Tree, pt: Type, where: String)(using Context): String = {
-
- def msg(shortForm: String)(headline: String = shortForm) = arg match {
- case arg: Trees.SearchFailureIdent[?] =>
- shortForm
- 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}."""
- }
- }
-
- def location(preposition: String) = if (where.isEmpty) "" else s" $preposition $where"
-
- /** 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)
- Trees.Literal(Constant(msg: String)) <- ann.argument(0)
- }
- yield msg
-
-
- arg.tpe match {
- case ambi: AmbiguousImplicits =>
- object AmbiguousImplicitMsg {
- def unapply(search: SearchSuccess): Option[String] =
- userDefinedMsg(search.ref.symbol, defn.ImplicitAmbiguousAnnot)
- }
-
- /** 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
+ /** @param arg Tree representing a failed result of implicit search
+ * @param pt Type for which an implicit value was searched
+ * @param where Description of where the search was performed. Might be empty
+ * @param paramSymWithMethodCallTree Symbol of the parameter for which the implicit was searched and tree of the method call that triggered the implicit search
+ */
+ def missingArgMsg(
+ arg: Tree,
+ pt: Type,
+ where: String,
+ paramSymWithMethodCallTree: Option[(Symbol, Tree)] = None
+ )(using Context): String = {
+ def findHiddenImplicitsCtx(c: Context): Context =
+ if c == NoContext then c
+ else c.freshOver(findHiddenImplicitsCtx(c.outer)).addMode(Mode.FindHiddenImplicits)
+
+ def ignoredInstanceNormalImport = arg.tpe match
+ case fail: SearchFailureType =>
+ if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then
+ inferImplicit(fail.expectedType, fail.argument, arg.span) match {
+ case s: SearchSuccess => Some(s)
+ case f: SearchFailure =>
+ f.reason match {
+ case ambi: AmbiguousImplicits => Some(ambi.alt1)
+ case r => None
+ }
}
- def resolveTypes(targs: List[Tree])(using Context) =
- targs.map(a => fullyDefinedType(a.tpe, "type argument", a.span))
-
- // 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 = closureBody(alt.tree) // the tree itself if not a closure
- val (_, targs, _) = decomposeCall(call)
- val args = resolveTypes(targs)(using ctx.fresh.setTyperState(alt.tstate))
- err.userDefinedErrorString(raw, params, args)
- }
-
- (ambi.alt1, ambi.alt2) match {
- case (alt @ AmbiguousImplicitMsg(msg), _) =>
- userDefinedAmbiguousImplicitMsg(alt, msg)
- case (_, alt @ AmbiguousImplicitMsg(msg)) =>
- userDefinedAmbiguousImplicitMsg(alt, msg)
- case _ =>
- msg(s"ambiguous implicit arguments: ${ambi.explanation}${location("of")}")(
- s"ambiguous implicit arguments of type ${pt.show} found${location("for")}")
- }
+ else
+ // It's unsafe to search for parts of the expected type if they are not fully defined,
+ // since these come with nested contexts that are lost at this point. See #7249 for an
+ // example where searching for a nested type causes an infinite loop.
+ None
- case _ =>
- val userDefined = userDefinedMsg(pt.typeSymbol, defn.ImplicitNotFoundAnnot).map(raw =>
- err.userDefinedErrorString(
- raw,
- pt.typeSymbol.typeParams.map(_.name.unexpandedName.toString),
- pt.widenExpr.dropDependentRefinement.argInfos))
-
- def hiddenImplicitsAddendum: String =
-
- def hiddenImplicitNote(s: SearchSuccess) =
- em"\n\nNote: given instance ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`."
-
- def findHiddenImplicitsCtx(c: Context): Context =
- if c == NoContext then c
- else c.freshOver(findHiddenImplicitsCtx(c.outer)).addMode(Mode.FindHiddenImplicits)
-
- val normalImports = arg.tpe match
- case fail: SearchFailureType =>
- if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then
- inferImplicit(fail.expectedType, fail.argument, arg.span)(
- using findHiddenImplicitsCtx(ctx)) match {
- case s: SearchSuccess => hiddenImplicitNote(s)
- case f: SearchFailure =>
- f.reason match {
- case ambi: AmbiguousImplicits => hiddenImplicitNote(ambi.alt1)
- case r => ""
- }
- }
- else
- // It's unsafe to search for parts of the expected type if they are not fully defined,
- // since these come with nested contexts that are lost at this point. See #7249 for an
- // example where searching for a nested type causes an infinite loop.
- ""
-
- def suggestedImports = importSuggestionAddendum(pt)
- if normalImports.isEmpty then suggestedImports else normalImports
- end hiddenImplicitsAddendum
-
- msg(userDefined.getOrElse(
- em"no implicit argument of type $pt was found${location("for")}"))() ++
- hiddenImplicitsAddendum
- }
+ val error = new ImplicitSearchError(arg, pt, where, paramSymWithMethodCallTree, ignoredInstanceNormalImport, importSuggestionAddendum(pt))
+ error.missingArgMsg
}
/** A string indicating the formal parameter corresponding to a missing argument */
diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala
index 60e96102c287..89b2ddff0c49 100644
--- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala
+++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala
@@ -23,7 +23,7 @@ import scala.util.matching.Regex._
import Constants.Constant
object RefChecks {
- import tpd.{Tree, MemberDef, Literal, Template, DefDef}
+ import tpd.{Tree, MemberDef, NamedArg, Literal, Template, DefDef}
val name: String = "refchecks"
@@ -957,18 +957,11 @@ object RefChecks {
* (i.e. they refer to a type variable that really occurs in the signature of the annotated symbol.)
*/
private object checkImplicitNotFoundAnnotation:
-
/** Warns if the class or trait has an @implicitNotFound annotation
* with invalid type variable references.
*/
def template(sd: SymDenotation)(using Context): Unit =
- for
- annotation <- sd.getAnnotation(defn.ImplicitNotFoundAnnot)
- l@Literal(c: Constant) <- annotation.argument(0)
- do forEachTypeVariableReferenceIn(c.stringValue) { case (ref, start) =>
- if !sd.typeParams.exists(_.denot.name.show == ref) then
- reportInvalidReferences(l, ref, start, sd)
- }
+ checkReferences(sd)
/** Warns if the def has parameters with an `@implicitNotFound` annotation
* with invalid type variable references.
@@ -977,26 +970,42 @@ object RefChecks {
for
paramSymss <- sd.paramSymss
param <- paramSymss
- do
- for
- annotation <- param.getAnnotation(defn.ImplicitNotFoundAnnot)
- l@Literal(c: Constant) <- annotation.argument(0)
- do forEachTypeVariableReferenceIn(c.stringValue) { case (ref, start) =>
- if !sd.paramSymss.flatten.exists(_.name.show == ref) then
- reportInvalidReferences(l, ref, start, sd)
- }
+ if param.isTerm
+ do checkReferences(param.denot)
+
+ private object PositionedStringLiteralArgument:
+ def unapply(tree: Tree): Option[(String, Span)] = tree match {
+ case l@Literal(Constant(s: String)) => Some((s, l.span))
+ case NamedArg(_, l@Literal(Constant(s: String))) => Some((s, l.span))
+ case _ => None
+ }
+
+ private def checkReferences(sd: SymDenotation)(using Context): Unit =
+ lazy val substitutableTypesNames =
+ ErrorReporting.substitutableTypeSymbolsInScope(sd.symbol).map(_.denot.name.show)
+ for
+ annotation <- sd.getAnnotation(defn.ImplicitNotFoundAnnot)
+ PositionedStringLiteralArgument(msg, span) <- annotation.argument(0)
+ do forEachTypeVariableReferenceIn(msg) { case (ref, start) =>
+ if !substitutableTypesNames.contains(ref) then
+ reportInvalidReference(span, ref, start, sd)
+ }
- /** Reports an invalid reference to a type variable `typeRef` that was found in `l` */
- private def reportInvalidReferences(
- l: Literal,
+ /** Reports an invalid reference to a type variable `typeRef` that was found in `span` */
+ private def reportInvalidReference(
+ span: Span,
typeRef: String,
- offsetInLiteral: Int,
+ variableOffsetinArgumentLiteral: Int,
sd: SymDenotation
)(using Context) =
- val msg = InvalidReferenceInImplicitNotFoundAnnotation(
- typeRef, if (sd.isConstructor) "the constructor" else sd.name.show)
- val span = l.span.shift(offsetInLiteral + 1) // +1 because of 0-based index
- val pos = ctx.source.atSpan(span.startPos)
+ val typeRefName = s"`$typeRef`"
+ val ownerName =
+ if sd.isType then s"type `${sd.name.show}`"
+ else if sd.owner.isConstructor then s"the constructor of `${sd.owner.owner.name.show}`"
+ else s"method `${sd.owner.name.show}`"
+ val msg = InvalidReferenceInImplicitNotFoundAnnotation(typeRefName, ownerName)
+ val startPos = span.shift(variableOffsetinArgumentLiteral + 1).startPos // +1 because of 0-based index
+ val pos = ctx.source.atSpan(startPos)
report.warning(msg, pos)
/** Calls the supplied function for each quoted reference to a type variable in
s
.
@@ -1013,10 +1022,15 @@ object RefChecks {
* @param f A function to apply to every pair of (\, \).
*/
private def forEachTypeVariableReferenceIn(s: String)(f: (String, Int) => Unit) =
- // matches quoted references such as "${(A)}", "${(Abc)}", etc.
- val reference = """(?<=\$\{)[a-zA-Z]+(?=\})""".r
- val matches = reference.findAllIn(s)
- for m <- matches do f(m, matches.start)
+ // matches quoted references such as "${A}", "${ Abc }", etc.
+ val referencePattern = """\$\{\s*([^}\s]+)\s*\}""".r
+ val matches = referencePattern.findAllIn(s)
+ for reference <- matches do
+ val referenceOffset = matches.start
+ val prefixlessReference = reference.replaceFirst("""\$\{\s*""", "")
+ val variableOffset = referenceOffset + reference.length - prefixlessReference.length
+ val variableName = prefixlessReference.replaceFirst("""\s*\}""", "")
+ f(variableName, variableOffset)
end checkImplicitNotFoundAnnotation
diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala
index a8ae701e3a27..fd1bdaab2a9c 100644
--- a/compiler/src/dotty/tools/dotc/typer/Typer.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala
@@ -2003,6 +2003,13 @@ class Typer extends Namer
checkThisConstrCall(rhs1)
}
+ if sym.is(Method) && sym.owner.denot.isRefinementClass then
+ for annot <- sym.paramSymss.flatten.filter(_.isTerm).flatMap(_.getAnnotation(defn.ImplicitNotFoundAnnot)) do
+ report.warning(
+ i"The annotation ${defn.ImplicitNotFoundAnnot} is not allowed on parameters of methods defined inside a refinement and it will have no effect",
+ annot.tree.sourcePos
+ )
+
val ddef2 = assignType(cpy.DefDef(ddef)(name, tparams1, vparamss1, tpt1, rhs1), sym)
checkSignatureRepeatedParam(sym)
@@ -3108,12 +3115,20 @@ class Typer extends Namer
val propFail = propagatedFailure(args)
def issueErrors(): Tree = {
+ def paramSymWithMethodTree(paramName: TermName) =
+ if tree.symbol.exists then
+ val paramSyms = tree.symbol.paramSymss.flatten.map(sym => sym.name -> sym).toMap
+ Some((paramSyms(paramName), tree))
+ else
+ None
+
wtp.paramNames.lazyZip(wtp.paramInfos).lazyZip(args).foreach { (paramName, formal, arg) =>
arg.tpe match {
case failure: SearchFailureType =>
report.error(
- missingArgMsg(arg, formal, implicitParamString(paramName, methodStr, tree)),
- tree.srcPos.endPos)
+ missingArgMsg(arg, formal, implicitParamString(paramName, methodStr, tree), paramSymWithMethodTree(paramName)),
+ tree.srcPos.endPos
+ )
case _ =>
}
}
diff --git a/tests/neg-custom-args/fatal-warnings/i4008.check b/tests/neg-custom-args/fatal-warnings/i4008.check
index c04a7949e9fd..a6e206f623d7 100644
--- a/tests/neg-custom-args/fatal-warnings/i4008.check
+++ b/tests/neg-custom-args/fatal-warnings/i4008.check
@@ -1,40 +1,40 @@
-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4008.scala:5:56 ---------------------------------------
5 |@annotation.implicitNotFound("An implicit ShouldWarn1[${B}] is not in scope") // error
| ^
- | Invalid reference to a type variable "B" found in the annotation argument.
- | The variable does not occur in the signature of ShouldWarn1.
+ | Invalid reference to a type variable `B` found in the annotation argument.
+ | The variable does not occur as a parameter in the scope of type `ShouldWarn1`.
-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4008.scala:9:56 ---------------------------------------
9 |@annotation.implicitNotFound("An implicit ShouldWarn2[${A}] is not in scope") // error
| ^
- | Invalid reference to a type variable "A" found in the annotation argument.
- | The variable does not occur in the signature of ShouldWarn2.
+ | Invalid reference to a type variable `A` found in the annotation argument.
+ | The variable does not occur as a parameter in the scope of type `ShouldWarn2`.
-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4008.scala:13:56 --------------------------------------
13 |@annotation.implicitNotFound("An implicit ShouldWarn3[${A},${B}] is not in scope") // error
| ^
- | Invalid reference to a type variable "A" found in the annotation argument.
- | The variable does not occur in the signature of ShouldWarn3.
+ | Invalid reference to a type variable `A` found in the annotation argument.
+ | The variable does not occur as a parameter in the scope of type `ShouldWarn3`.
-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4008.scala:17:56 --------------------------------------
17 |@annotation.implicitNotFound("An implicit ShouldWarn4[${A},${B}] is not in scope") // error // error
| ^
- | Invalid reference to a type variable "A" found in the annotation argument.
- | The variable does not occur in the signature of ShouldWarn4.
+ | Invalid reference to a type variable `A` found in the annotation argument.
+ | The variable does not occur as a parameter in the scope of type `ShouldWarn4`.
-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4008.scala:17:61 --------------------------------------
17 |@annotation.implicitNotFound("An implicit ShouldWarn4[${A},${B}] is not in scope") // error // error
| ^
- | Invalid reference to a type variable "B" found in the annotation argument.
- | The variable does not occur in the signature of ShouldWarn4.
+ | Invalid reference to a type variable `B` found in the annotation argument.
+ | The variable does not occur as a parameter in the scope of type `ShouldWarn4`.
-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4008.scala:21:61 --------------------------------------
21 |@annotation.implicitNotFound("An implicit ShouldWarn5[${C},${Abc}] is not in scope") // error
| ^
- | Invalid reference to a type variable "Abc" found in the annotation argument.
- | The variable does not occur in the signature of ShouldWarn5.
+ | Invalid reference to a type variable `Abc` found in the annotation argument.
+ | The variable does not occur as a parameter in the scope of type `ShouldWarn5`.
-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4008.scala:44:54 --------------------------------------
44 |class C[A](using @annotation.implicitNotFound("No C[${B}] found") c: Class[A]) // error
| ^
- | Invalid reference to a type variable "B" found in the annotation argument.
- | The variable does not occur in the signature of the constructor.
+ | Invalid reference to a type variable `B` found in the annotation argument.
+ | The variable does not occur as a parameter in the scope of the constructor of `C`.
-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4008.scala:46:62 --------------------------------------
46 |def someMethod1[A](using @annotation.implicitNotFound("No C[${B}] found") sc: C[A]) = 0 // error
| ^
- | Invalid reference to a type variable "B" found in the annotation argument.
- | The variable does not occur in the signature of someMethod1.
+ | Invalid reference to a type variable `B` found in the annotation argument.
+ | The variable does not occur as a parameter in the scope of method `someMethod1`.
diff --git a/tests/neg-custom-args/fatal-warnings/i4986b.check b/tests/neg-custom-args/fatal-warnings/i4986b.check
new file mode 100644
index 000000000000..2e74c29b077c
--- /dev/null
+++ b/tests/neg-custom-args/fatal-warnings/i4986b.check
@@ -0,0 +1,40 @@
+-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4986b.scala:3:65 --------------------------------------
+3 |@implicitNotFound(msg = "Cannot construct a collection of type ${Too} with elements of type ${Elem} based on a collection of type ${From}.") // error // error
+ | ^
+ | Invalid reference to a type variable `Too` found in the annotation argument.
+ | The variable does not occur as a parameter in the scope of type `Meh`.
+-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4986b.scala:3:94 --------------------------------------
+3 |@implicitNotFound(msg = "Cannot construct a collection of type ${Too} with elements of type ${Elem} based on a collection of type ${From}.") // error // error
+ | ^
+ | Invalid reference to a type variable `Elem` found in the annotation argument.
+ | The variable does not occur as a parameter in the scope of type `Meh`.
+-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4986b.scala:6:71 --------------------------------------
+6 |@implicitNotFound(msg = "Cannot construct a collection of type ${To} ${Elem}.") // error
+ | ^
+ | Invalid reference to a type variable `Elem` found in the annotation argument.
+ | The variable does not occur as a parameter in the scope of type `Meh2`.
+-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4986b.scala:9:46 --------------------------------------
+9 |class C[T](implicit @implicitNotFound("No C[${t}] available") t: T) // error
+ | ^
+ | Invalid reference to a type variable `t` found in the annotation argument.
+ | The variable does not occur as a parameter in the scope of the constructor of `C`.
+-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4986b.scala:12:54 -------------------------------------
+12 | def m[Aaa](implicit @implicitNotFound("I see no C[${Uuh}]") theC: C[Aaa]) = ??? // error
+ | ^
+ | Invalid reference to a type variable `Uuh` found in the annotation argument.
+ | The variable does not occur as a parameter in the scope of method `m`.
+-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4986b.scala:18:73 -------------------------------------
+18 | def m[S](implicit @implicitNotFound("${X} ${Y} ${ Z } ${R} ${S} -- ${XX} ${ZZ} ${ Nix }") i: Int) = ??? // error // error // error
+ | ^
+ | Invalid reference to a type variable `XX` found in the annotation argument.
+ | The variable does not occur as a parameter in the scope of method `m`.
+-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4986b.scala:18:79 -------------------------------------
+18 | def m[S](implicit @implicitNotFound("${X} ${Y} ${ Z } ${R} ${S} -- ${XX} ${ZZ} ${ Nix }") i: Int) = ??? // error // error // error
+ | ^
+ | Invalid reference to a type variable `ZZ` found in the annotation argument.
+ | The variable does not occur as a parameter in the scope of method `m`.
+-- [E158] Reference Error: tests/neg-custom-args/fatal-warnings/i4986b.scala:18:86 -------------------------------------
+18 | def m[S](implicit @implicitNotFound("${X} ${Y} ${ Z } ${R} ${S} -- ${XX} ${ZZ} ${ Nix }") i: Int) = ??? // error // error // error
+ | ^
+ | Invalid reference to a type variable `Nix` found in the annotation argument.
+ | The variable does not occur as a parameter in the scope of method `m`.
diff --git a/tests/neg-custom-args/fatal-warnings/i4986b.scala b/tests/neg-custom-args/fatal-warnings/i4986b.scala
new file mode 100644
index 000000000000..e4ee1e64e8fe
--- /dev/null
+++ b/tests/neg-custom-args/fatal-warnings/i4986b.scala
@@ -0,0 +1,20 @@
+import scala.annotation.implicitNotFound
+
+@implicitNotFound(msg = "Cannot construct a collection of type ${Too} with elements of type ${Elem} based on a collection of type ${From}.") // error // error
+trait Meh[-From, +To]
+
+@implicitNotFound(msg = "Cannot construct a collection of type ${To} ${Elem}.") // error
+trait Meh2[-From, +To]
+
+class C[T](implicit @implicitNotFound("No C[${t}] available") t: T) // error
+
+trait T {
+ def m[Aaa](implicit @implicitNotFound("I see no C[${Uuh}]") theC: C[Aaa]) = ??? // error
+ def n[Aaa](implicit @implicitNotFound("I see no C[${Aaa}]") theC: C[Aaa]) = ???
+}
+
+trait U[X, Y[_], Z[_, ZZ]] {
+ class I[R] {
+ def m[S](implicit @implicitNotFound("${X} ${Y} ${ Z } ${R} ${S} -- ${XX} ${ZZ} ${ Nix }") i: Int) = ??? // error // error // error
+ }
+}
diff --git a/tests/neg-custom-args/fatal-warnings/i4986d.scala b/tests/neg-custom-args/fatal-warnings/i4986d.scala
new file mode 100644
index 000000000000..74a3c01aaa71
--- /dev/null
+++ b/tests/neg-custom-args/fatal-warnings/i4986d.scala
@@ -0,0 +1,22 @@
+trait Foo[A]
+
+type Fooable[A] = {
+ def foo(implicit @annotation.implicitNotFound("There's no Foo[${A}]") ev: Foo[A]): Any // error
+
+ type InnerFooable = {
+ def foo(implicit @annotation.implicitNotFound("There's no Foo[${A}]") ev: Foo[A]): Any // error
+ }
+}
+
+object Fooable {
+ (null: Fooable[Long]).foo // error
+ (null: Fooable[Long]#InnerFooable).foo // error
+}
+
+
+@annotation.implicitNotFound("There's no Bar[${A}]")
+trait Bar[A]
+
+type Barable[A] = {
+ def bar(implicit ev: Bar[A]): Any // ok
+}
diff --git a/tests/neg/i4986a.check b/tests/neg/i4986a.check
new file mode 100644
index 000000000000..5375d0fd002b
--- /dev/null
+++ b/tests/neg/i4986a.check
@@ -0,0 +1,9 @@
+-- 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]..
+ |I found:
+ |
+ | collection.BuildFrom.buildFromIterableOps[Nothing, Nothing, Nothing]
+ |
+ |But method buildFromIterableOps in trait BuildFromLowPriority2 does not match type collection.BuildFrom[List[Int], Int, List[String]].
diff --git a/tests/neg/i4986a.scala b/tests/neg/i4986a.scala
new file mode 100644
index 000000000000..dcf461a4b1e7
--- /dev/null
+++ b/tests/neg/i4986a.scala
@@ -0,0 +1,7 @@
+class Lst[+A] {
+ def map[B, That](f: A => B)(implicit bf: collection.BuildFrom[List[A], B, That]): That = ???
+}
+
+object Test {
+ def foo(l: Lst[Int]) = l.map[Int, List[String]](x => 1) // error
+}
diff --git a/tests/neg/i4986c.check b/tests/neg/i4986c.check
new file mode 100644
index 000000000000..b93a5520b021
--- /dev/null
+++ b/tests/neg/i4986c.check
@@ -0,0 +1,64 @@
+-- Error: tests/neg/i4986c.scala:38:8 ----------------------------------------------------------------------------------
+38 | test.f // error
+ | ^
+ | Missing X$Y for Test[Char]
+-- 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 ---------------------------------------------------------------------------------
+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 ---------------------------------------------------------------------------------
+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 ---------------------------------------------------------------------------------
+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 ---------------------------------------------------------------------------------
+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 ---------------------------------------------------------------------------------
+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]]]{MyType = BigDecimal}#OuterMember
+-- 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]]]{MyType = BigDecimal}#Inner[Byte, Seq]#InnerMember from Outer[Option[String] | List[Iterable[Char]]] with OuterMember = pkg.Outer[Option[String] | List[Iterable[Char]]]{MyType = BigDecimal}#OuterMember
+-- 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 ---------------------------------------------------------------------------------
+52 | implicitly[Outer[Int] { type OuterMember = Long }] // error
+ | ^
+ | Missing Outer[Int] with OuterMember = Long
+-- 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 --------------------------------------------------------------------------------
+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 ---------------------------------------------------------------------------------
+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 ---------------------------------------------------------------------------------
+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 ---------------------------------------------------------------------------------
+58 | implicitly[U[Int, Option, Map]] // error
+ | ^
+ | There's no U[Int, Option, Map]
+-- Error: tests/neg/i4986c.scala:62:19 ---------------------------------------------------------------------------------
+62 | i.m[Option[Long]] // error
+ | ^
+ | String; List; [A, _$6] =>> List[Option[?]]; Int; Option[Long];
diff --git a/tests/neg/i4986c.scala b/tests/neg/i4986c.scala
new file mode 100644
index 000000000000..2176c6019c0a
--- /dev/null
+++ b/tests/neg/i4986c.scala
@@ -0,0 +1,63 @@
+package pkg
+
+import annotation._
+
+class myAnnot extends StaticAnnotation
+
+@implicitNotFound("Missing Outer[${ A }] with OuterMember = ${OuterMember}")
+class Outer[A] {
+ type OuterMember
+
+ @implicitNotFound("Missing Inner[${B}, ${C}] with InnerMember = ${InnerMember} from Outer[${ A }] with OuterMember = ${OuterMember}")
+ class Inner[B, C[_]] {
+ type InnerMember
+ }
+}
+
+trait X$Y
+
+@implicitNotFound("There's no U[${X}, ${Y}, ${Z}]")
+trait U[X, Y[_], Z[_, ZZ]] {
+ class I[R] {
+ def m[S](implicit @implicitNotFound("${X}; ${Y}; ${ Z }; ${R}; ${S}; ${XX}") i: Int) = ???
+ }
+}
+
+class Test[A] {
+ def f(implicit @implicitNotFound("Missing X$Y for Test[${A}]") xy: X$Y) = ???
+ def g[B: Outer] = ???
+ def h[B](implicit outer: Outer[B]) = ???
+ def i[B](implicit @implicitNotFound("Missing implicit outer param of type Outer[${B}] for Test[${A}]") outer: Outer[B]) = ???
+ def j[B, C, D[_]](implicit inner: Outer[B]#Inner[C, D]) = ???
+ def k[B, C, D[_]](implicit @implicitNotFound("Missing implicit inner param of type Outer[${B}]#Inner[${C}, ${D}] for Test[${A}]") inner: Outer[B]#Inner[C, D]) = ???
+}
+
+object Test {
+ val test = new Test[Char]
+
+ test.f // error
+ test.g[Int] // error
+ test.h[X$Y] // error
+ test.i[Option[String]] // error
+ test.j[(Long, Long), Int | String, Array] // error
+ test.k[Either[String, Any], Seq[Seq[Char]], Vector] // error
+
+ implicitly[Outer[Option[String] | List[Iterable[Char]]] { type MyType = BigDecimal }] // error
+ implicitly[(Outer[Option[String] | List[Iterable[Char]]] { type MyType = BigDecimal })#Inner[Byte, Seq]] // error
+ implicitly[Outer[Int] @myAnnot] // error
+
+ val outer = new Outer[Int] { type OuterMember = Long }
+ val inner = new outer.Inner[Long, Iterator] { type InnerMember = Byte }
+
+ implicitly[Outer[Int] { type OuterMember = Long }] // error
+ implicitly[outer.type] // error
+ implicitly[(Outer[Int] { type OuterMember = Long })#Inner[Long, Iterator] { type InnerMember = Byte }] // error
+ implicitly[outer.Inner[Long, Iterator] { type InnerMember = Byte }] // error
+ implicitly[inner.type] // error
+
+ implicitly[U[Int, Option, Map]] // error
+
+ val u = new U[String, List, [A, _] =>> List[Option[_]]] { }
+ val i = new u.I[Int]
+ i.m[Option[Long]] // error
+}
diff --git a/tests/untried/neg/t2462a.check b/tests/untried/neg/t2462a.check
deleted file mode 100644
index 86d74b86d406..000000000000
--- a/tests/untried/neg/t2462a.check
+++ /dev/null
@@ -1,4 +0,0 @@
-t2462a.scala:2: error: Cannot construct a collection of type List[String] with elements of type Int based on a collection of type List[Int].
- List(1,2,3).map[Int, List[String]](x => 1)
- ^
-one error found
diff --git a/tests/untried/neg/t2462a.scala b/tests/untried/neg/t2462a.scala
deleted file mode 100644
index 5e49314d53b0..000000000000
--- a/tests/untried/neg/t2462a.scala
+++ /dev/null
@@ -1,3 +0,0 @@
-object Test {
- List(1,2,3).map[Int, List[String]](x => 1)
-}
diff --git a/tests/untried/neg/t2462b.check b/tests/untried/neg/t2462b.check
deleted file mode 100644
index b3b8007a93a2..000000000000
--- a/tests/untried/neg/t2462b.check
+++ /dev/null
@@ -1,11 +0,0 @@
-t2462b.scala:6: warning: Invalid implicitNotFound message for trait Meh in package test:
-The type parameters Too, Elem referenced in the message of the @implicitNotFound annotation are not defined by trait Meh.
-trait Meh[-From, +To]
- ^
-t2462b.scala:9: warning: Invalid implicitNotFound message for trait Meh2 in package test:
-The type parameter Elem referenced in the message of the @implicitNotFound annotation is not defined by trait Meh2.
-trait Meh2[-From, +To]
- ^
-error: No warnings can be incurred under -Xfatal-warnings.
-two warnings found
-one error found
diff --git a/tests/untried/neg/t2462b.flags b/tests/untried/neg/t2462b.flags
deleted file mode 100644
index 85d8eb2ba295..000000000000
--- a/tests/untried/neg/t2462b.flags
+++ /dev/null
@@ -1 +0,0 @@
--Xfatal-warnings
diff --git a/tests/untried/neg/t2462b.scala b/tests/untried/neg/t2462b.scala
deleted file mode 100644
index 576db4bd3f33..000000000000
--- a/tests/untried/neg/t2462b.scala
+++ /dev/null
@@ -1,9 +0,0 @@
-package test
-
-import scala.annotation.implicitNotFound
-
-@implicitNotFound(msg = "Cannot construct a collection of type ${Too} with elements of type ${Elem} based on a collection of type ${From}.")
-trait Meh[-From, +To]
-
-@implicitNotFound(msg = "Cannot construct a collection of type ${To} ${Elem}.")
-trait Meh2[-From, +To]
diff --git a/tests/untried/neg/t2462c.check b/tests/untried/neg/t2462c.check
deleted file mode 100644
index edeead55d60c..000000000000
--- a/tests/untried/neg/t2462c.check
+++ /dev/null
@@ -1,7 +0,0 @@
-t2462c.scala:18: error: No C of X$Y
- f[X$Y]
- ^
-t2462c.scala:24: error: No C of Foo[Int]
- f[Foo[Int]]
- ^
-two errors found
diff --git a/tests/untried/neg/t2462c.flags b/tests/untried/neg/t2462c.flags
deleted file mode 100644
index 85d8eb2ba295..000000000000
--- a/tests/untried/neg/t2462c.flags
+++ /dev/null
@@ -1 +0,0 @@
--Xfatal-warnings
diff --git a/tests/untried/neg/t2462c.scala b/tests/untried/neg/t2462c.scala
deleted file mode 100644
index acf04afba954..000000000000
--- a/tests/untried/neg/t2462c.scala
+++ /dev/null
@@ -1,25 +0,0 @@
-
-import annotation._
-
-@implicitNotFound("No C of ${ A }")
-class C[A]
-
-trait X$Y
-/* using the $$ separator for expanded names is unwise
-trait X$$Y
-trait X$$$Y
-trait X$$$$Y
- */
-
-trait Foo[A]
-
-class Test {
- def f[A: C] = ???
- f[X$Y]
-/* using the $$ separator for expanded names is unwise
- f[X$$Y]
- f[X$$$Y]
- f[X$$$$Y]
- */
- f[Foo[Int]]
-}