Permalink
Browse files

Fix `getLogger` for higher-kinded types

Previously a top-level class declaration would fail on `getLogger` if
the class had a higher-kinded type parameter (e.g., `class A[F[_]]`).
Scala doesn't have a trivial way to say "I don't care about the
parameter" in this case, as we can with `WildcardType` in other cases.
So we simply fall back to getting the logger by name in this case.
  • Loading branch information...
sarahgerweck committed Oct 2, 2017
1 parent 43f196a commit be67f22e7f9c73e2adfdd8d326c15bfab9ce31a5
View
@@ -47,3 +47,6 @@ changes to how it's coded or built, see the Git history.
### 1.4.0
* Update to Scala 2.12.3.
* Update to SBT 1.0.2.
* Fix [#18](https://github.com/Log4s/log4s/issues/18), which prevented
`getLogger` from correctly getting the logger for a top-level class with
a higher-kinded type parameter.
@@ -46,17 +46,22 @@ private[log4s] object LoggerMacros {
def loggerByType(s: Symbol) = {
val typeSymbol = (if (cls.isModule) cls.asModule.moduleClass else cls).asClass
val typeConstructor = {
val polyArgs = for (tpar <- typeSymbol.typeParams) yield WildcardType
if (polyArgs.isEmpty) {
typeSymbol.toTypeConstructor
} else {
appliedType(typeSymbol.toTypeConstructor, polyArgs)
if (typeSymbol.typeParams.exists(_.asType.typeParams.nonEmpty)) {
/* We have higher-kinded parameters: fall back to constructing the logger by name */
loggerBySymbolName(s)
} else {
val typeConstructor = {
val polyArgs = for (tpar <- typeSymbol.typeParams) yield WildcardType
if (polyArgs.isEmpty) {
typeSymbol.toTypeConstructor
} else {
appliedType(typeSymbol.toTypeConstructor, polyArgs)
}
}
}
val expr = c.Expr[Class[_]](Literal(Constant(typeConstructor)))
reify { new Logger(getJLogger(expr.splice)) }
val expr = c.Expr[Class[_]](Literal(Constant(typeConstructor)))
reify { new Logger(getJLogger(expr.splice)) }
}
}
@inline def isInnerClass(s: Symbol) = {
@@ -64,9 +64,15 @@ private[log4s] object LoggerMacros {
if (typeParams.isEmpty) {
loggerByParam(q"classOf[$typeSymbol]")
} else {
val typeArgs = List.fill(typeParams.length)(WildcardType)
val typeConstructor = tq"$typeSymbol[..${typeArgs}]"
loggerByParam(q"classOf[$typeConstructor]")
if (typeParams.exists(_.asType.typeParams.nonEmpty)) {
/* We have at least one higher-kinded type: fall back to by-name logger construction, as
* there's no simple way to declare a higher-kinded type with an "any" parameter. */
loggerBySymbolName(s)
} else {
val typeArgs = List.fill(typeParams.length)(WildcardType)
val typeConstructor = tq"$typeSymbol[..${typeArgs}]"
loggerByParam(q"classOf[$typeConstructor]")
}
}
}
@@ -1,5 +1,7 @@
package org.log4s
import language.higherKinds
import org.scalatest._
/** Test suite for the behavior of `getLogger`.
@@ -103,6 +105,16 @@ class GetLoggerSpec extends FlatSpec with Matchers with GivenWhenThen with Logge
it should "support explicit logger names" in {
getLogger("a.b.c").name shouldEqual "a.b.c"
}
it should "support higher-kinded types" in {
val hkt = new GetLoggerSpecTLKind1[Seq]
hkt.logger.name shouldEqual "org.log4s.GetLoggerSpecTLKind1"
}
it should "support higher-kinded nested types" in {
val hkt = new GetLoggerSpecKinds.NestedKind1[Seq]
hkt.logger.name shouldEqual "org.log4s.GetLoggerSpecKinds.NestedKind1"
}
}
private class GetLoggerSpecParam[A] {
@@ -141,3 +153,13 @@ private object GetLoggerSpecTLO {
package object packageScoped {
val logger = org.log4s.getLogger
}
private object GetLoggerSpecKinds {
class NestedKind1[F[_]] {
val logger = getLogger
}
}
private class GetLoggerSpecTLKind1[F[_]] {
val logger = getLogger
}

0 comments on commit be67f22

Please sign in to comment.