Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -143,15 +143,15 @@ trait ContextErrors {
def errMsg = {
val paramName = param.name
val paramTp = param.tpe
def evOrParam = (
def evOrParam =
if (paramName startsWith nme.EVIDENCE_PARAM_PREFIX)
"evidence parameter of type"
else
s"parameter $paramName:")
paramTp.typeSymbolDirect match {
case ImplicitNotFoundMsg(msg) => msg.format(paramName, paramTp)
case _ => s"could not find implicit value for $evOrParam $paramTp"
}
s"parameter $paramName:"

ImplicitNotFoundMsg.unapply(param).map(_.formatParameterMessage(tree))
.orElse(ImplicitNotFoundMsg.unapply(paramTp.typeSymbolDirect).map(_.formatDefSiteMessage(paramTp)))
.getOrElse(s"could not find implicit value for $evOrParam $paramTp")
}
issueNormalTypeError(tree, errMsg)
}
Expand Down Expand Up @@ -1385,8 +1385,8 @@ trait ContextErrors {

context0.issueAmbiguousError(AmbiguousImplicitTypeError(tree,
(info1.sym, info2.sym) match {
case (ImplicitAmbiguousMsg(msg), _) => msg.format(treeTypeArgs(tree1))
case (_, ImplicitAmbiguousMsg(msg)) => msg.format(treeTypeArgs(tree2))
case (ImplicitAmbiguousMsg(msg), _) => msg.formatDefSiteMessage(treeTypeArgs(tree1))
case (_, ImplicitAmbiguousMsg(msg)) => msg.formatDefSiteMessage(treeTypeArgs(tree2))
case (_, _) if isView => viewMsg
case (_, _) => s"ambiguous implicit values:\n${coreMsg}match expected type $pt"
}
Expand Down
68 changes: 59 additions & 9 deletions src/compiler/scala/tools/nsc/typechecker/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1595,7 +1595,7 @@ trait Implicits {
}

class ImplicitAnnotationMsg(f: Symbol => Option[String], clazz: Symbol, annotationName: String) {
def unapply(sym: Symbol): Option[(Message)] = f(sym) match {
def unapply(sym: Symbol): Option[Message] = f(sym) match {
case Some(m) => Some(new Message(sym, m, annotationName))
case None if sym.isAliasType =>
// perform exactly one step of dealiasing
Expand Down Expand Up @@ -1628,25 +1628,75 @@ trait Implicits {
// #3915: need to quote replacement string since it may include $'s (such as the interpreter's $iw)
})

private lazy val typeParamNames: List[String] = sym.typeParams.map(_.decodedName)
def referencedTypeParams: List[String] = Intersobralator.findAllMatchIn(msg).map(_.group(1)).distinct.toList

private def symTypeParamNames: List[String] = sym.typeParams.map(_.decodedName)

def lookupTypeParam(name: String): Symbol = {
val n = newTypeName(name)
var r: Symbol = NoSymbol
var o = sym.owner
while (r == NoSymbol && o != NoSymbol) {
o.typeParams.find(_.name == n) match {
case Some(p) => r = p
case _ =>
do { o = o.owner } while (!(o.isClass || o.isMethod || o == NoSymbol))
}
}
r
}

private def typeArgsAtSym(paramTp: Type) = paramTp.baseType(sym).typeArgs

def format(paramName: Name, paramTp: Type): String = format(typeArgsAtSym(paramTp) map (_.toString))
def formatDefSiteMessage(paramTp: Type): String =
formatDefSiteMessage(typeArgsAtSym(paramTp) map (_.toString))

def format(typeArgs: List[String]): String =
interpolate(msg, Map((typeParamNames zip typeArgs): _*)) // TODO: give access to the name and type of the implicit argument, etc?
def formatDefSiteMessage(typeArgs: List[String]): String =
interpolate(msg, Map(symTypeParamNames zip typeArgs: _*))

def formatParameterMessage(fun: Tree): String = {
val paramNames = referencedTypeParams
val paramSyms = paramNames.map(lookupTypeParam).filterNot(_ == NoSymbol)
val paramTypeRefs = paramSyms.map(_.typeConstructor.etaExpand) // make polytypes for type constructors -- we'll abbreviate them below
val prefix = fun match {
case treeInfo.Applied(Select(qual, _), _, _) => qual.tpe
case _ => NoType
}

val argTypes1 = if (prefix == NoType) paramTypeRefs else paramTypeRefs.map(t => t.asSeenFrom(prefix, fun.symbol.owner))
val argTypes2 = fun match {
case TypeApply(_, targs) => argTypes1.map(_.instantiateTypeParams(fun.symbol.info.typeParams, targs.map(_.tpe)))
case _ => argTypes1
}

val argTypes = argTypes2.map{
case PolyType(tps, tr@TypeRef(_, _, tprefs)) =>
if (tps.corresponds(tprefs)((p, r) => p == r.typeSymbol)) tr.typeConstructor.toString
else {
val freshTpars = tps.mapConserve { case p if p.name == tpnme.WILDCARD => p.cloneSymbol.setName(newTypeName("?T" + tps.indexOf(p))) case p => p }
freshTpars.map(_.name).mkString("[", ", ", "] -> ") + tr.instantiateTypeParams(tps, freshTpars.map(_.typeConstructor)).toString
}

case tp => tp.toString
}
interpolate(msg, Map(paramNames zip argTypes: _*))
}

def validate: Option[String] = {
val refs = Intersobralator.findAllMatchIn(msg).map(_ group 1).toSeq.distinct
val decls = typeParamNames.toSeq.distinct
val refs = referencedTypeParams
val isMessageOnParameter = sym.isParameter
val decls =
if (isMessageOnParameter) referencedTypeParams.filterNot(p => lookupTypeParam(p) == NoSymbol)
else symTypeParamNames.distinct

(refs.diff(decls)) match {
refs.diff(decls) match {
case s if s.isEmpty => None
case unboundNames =>
val singular = unboundNames.size == 1
val ess = if (singular) "" else "s"
val bee = if (singular) "is" else "are"
Some(s"The type parameter$ess ${unboundNames mkString ", "} referenced in the message of the @$annotationName annotation $bee not defined by $sym.")
val where = if (isMessageOnParameter) s"in scope" else s"defined by $sym"
Some(s"The type parameter$ess ${unboundNames mkString ", "} referenced in the message of the @$annotationName annotation $bee not $where.")
}
}
}
Expand Down
42 changes: 38 additions & 4 deletions src/library/scala/annotation/implicitNotFound.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,44 @@ package scala.annotation

/**
* To customize the error message that's emitted when an implicit of type
* C[T1,..., TN] cannot be found, annotate the class C with @implicitNotFound.
* Assuming C has type parameters X1,..., XN, the error message will be the
* result of replacing all occurrences of ${Xi} in the string msg with the
* string representation of the corresponding type argument Ti. *
* `C[T1,..., TN]` cannot be found, annotate the class `C` with `@implicitNotFound`.
* Assuming `C` has type parameters `X1, ..., XN`, the error message will be the
* result of replacing all occurrences of `${Xi}` in the string `msg` with the
* string representation of the corresponding type argument `Ti`.
*
* The annotation can also be attached to implicit parameters. In this case, `${Xi}`
* can refer to type parameters in the current scope. The `@implicitNotFound` message
* on the parameter takes precedence over the one on the parameter's type.
*
* {{{
* import scala.annotation.implicitNotFound
*
* @implicitNotFound("Could not find an implicit C[${T}, ${U}]")
* class C[T, U]
*
* class K[A] {
* def m[B](implicit c: C[List[A], B]) = 0
* def n[B](implicit @implicitNotFound("Specific message for C of list of ${A} and ${B}") c: C[List[A], B]) = 1
* }
*
* object Test {
* val k = new K[Int]
* k.m[String]
* k.n[String]
* }
* }}}
*
* The compiler issues the following error messages:
*
* <pre>
* Test.scala:13: error: Could not find an implicit C[List[Int], String]
* k.m[String]
* ^
* Test.scala:14: error: Specific message for C of list of Int and String
* k.n[String]
* ^
* </pre>
*
*
* @author Adriaan Moors
* @since 2.8.1
Expand Down
11 changes: 10 additions & 1 deletion test/files/neg/t2462b.check
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ t2462b.scala:9: warning: Invalid implicitNotFound message for trait Meh2 in pack
The type parameter Elem referenced in the message of the @implicitNotFound annotation is not defined by trait Meh2.
trait Meh2[-From, +To]
^
t2462b.scala:13: warning: Invalid implicitNotFound message for value theC:
The type parameter Uuh referenced in the message of the @implicitNotFound annotation is not in scope.
def m[Aaa](implicit @implicitNotFound("I see no C[${Uuh}]") theC: C[Aaa]) = ???
^
t2462b.scala:19: warning: Invalid implicitNotFound message for value i:
The type parameters XX, ZZ, Nix referenced in the message of the @implicitNotFound annotation are not in scope.
def m[S](implicit @implicitNotFound("${X} ${Y} ${ Z } ${R} ${S} -- ${XX} ${ZZ} ${ Nix }") i: Int) = ???
^
warning: there were two feature warnings; re-run with -feature for details
error: No warnings can be incurred under -Xfatal-warnings.
two warnings found
5 warnings found
one error found
12 changes: 12 additions & 0 deletions test/files/neg/t2462b.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,15 @@ trait Meh[-From, +To]

@implicitNotFound(msg = "Cannot construct a collection of type ${To} ${Elem}.")
trait Meh2[-From, +To]

class C[T]
trait T {
def m[Aaa](implicit @implicitNotFound("I see no C[${Uuh}]") theC: C[Aaa]) = ???
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) = ???
}
}
15 changes: 12 additions & 3 deletions test/files/neg/t2462c.check
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
t2462c.scala:18: error: No C of X$Y
t2462c.scala:24: error: No C of X$Y
f[X$Y]
^
t2462c.scala:24: error: No C of Foo[Int]
t2462c.scala:30: error: No C of Foo[Int]
f[Foo[Int]]
^
two errors found
t2462c.scala:33: error: No C of Foo[Int]
g[Foo[Int]]
^
t2462c.scala:36: error: I see no C[Foo[Int]]
h[Foo[Int]]
^
t2462c.scala:40: error: String List [?T0, ZZ] -> List[C[_]] Int Option[Long] -- .
i.m[Option[Long]]
^
5 errors found
16 changes: 16 additions & 0 deletions test/files/neg/t2462c.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ trait X$$$$Y

trait Foo[A]

trait U[X, Y[_], Z[_, ZZ]] {
class I[R] {
def m[S](implicit @implicitNotFound("${X} ${Y} ${ Z } ${R} ${S} -- ${XX}.") i: Int) = ???
}
}

class Test {
def f[A: C] = ???
f[X$Y]
Expand All @@ -22,4 +28,14 @@ class Test {
f[X$$$$Y]
*/
f[Foo[Int]]

def g[Aaa](implicit theC: C[Aaa]) = ???
g[Foo[Int]]

def h[Aaa](implicit @implicitNotFound("I see no C[${Aaa}]") theC: C[Aaa]) = ???
h[Foo[Int]]

val u = new U[String, List, ({type T[A, _] = List[C[_]]})#T] { }
val i = new u.I[Int]
i.m[Option[Long]]
}