Skip to content

Commit 27b4331

Browse files
committed
Use better way to find a companion
1 parent 4fbc4de commit 27b4331

File tree

1 file changed

+18
-46
lines changed
  • jsoniter-scala-macros/shared/src/main/scala/com/github/plokhotnyuk/jsoniter_scala/macros

1 file changed

+18
-46
lines changed

jsoniter-scala-macros/shared/src/main/scala/com/github/plokhotnyuk/jsoniter_scala/macros/JsonCodecMaker.scala

Lines changed: 18 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import scala.collection.{BitSet, immutable, mutable}
1111
import scala.collection.mutable.ArrayBuffer
1212
import scala.language.experimental.macros
1313
import scala.reflect.NameTransformer
14-
import scala.reflect.macros.contexts.Context
1514
import scala.reflect.macros.blackbox
1615
import scala.util.control.NonFatal
1716

@@ -364,6 +363,7 @@ object JsonCodecMaker {
364363

365364
private[this] def make[A: c.WeakTypeTag](c: blackbox.Context)(cfg: CodecMakerConfig): c.Expr[JsonValueCodec[A]] = {
366365
import c.universe._
366+
import c.internal._
367367

368368
def fail(msg: String): Nothing = c.abort(c.enclosingPosition, msg)
369369

@@ -441,40 +441,20 @@ object JsonCodecMaker {
441441
}
442442

443443
def companion(tpe: Type): Symbol = {
444-
// Borrowed and refactored from Chimney: https://github.com/scalalandio/chimney/blob/master/chimney/src/main/scala/io/scalaland/chimney/internal/CompanionUtils.scala#L10-L63
445-
// Copied from Magnolia: https://github.com/propensive/magnolia/blob/master/core/shared/src/main/scala/globalutil.scala
446-
// From Shapeless: https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/generic.scala#L698
447-
// Cut-n-pasted (with most original comments) and slightly adapted from https://github.com/scalamacros/paradise/blob/c14c634923313dd03f4f483be3d7782a9b56de0e/plugin/src/main/scala/org/scalamacros/paradise/typechecker/Namers.scala#L568-L613
448-
def patchedCompanionRef(tpe: Type): Tree = {
449-
val global = c.universe.asInstanceOf[scala.tools.nsc.Global]
450-
val globalType = tpe.asInstanceOf[global.Type]
451-
val original = globalType.typeSymbol
452-
global.gen.mkAttributedRef(globalType.prefix, original.companion.orElse {
453-
import global._
454-
val name = original.name.companionName
455-
val expectedOwner = original.owner
456-
var ctx = c.asInstanceOf[Context].callsiteTyper.asInstanceOf[global.analyzer.Typer].context
457-
var res: Symbol = NoSymbol
458-
while (res == NoSymbol && ctx.outer != ctx) {
459-
// NOTE: original implementation says `val s = ctx.scope lookup name`
460-
// but we can't use it, because Scope.lookup returns wrong results when the lookup is ambiguous
461-
// and that triggers https://github.com/scalamacros/paradise/issues/64
462-
val s = ctx.scope.lookupAll(name)
463-
.filter(sym => (original.isTerm || sym.hasModuleFlag) && sym.isCoDefinedWith(original)).toList match {
464-
case Nil => NoSymbol
465-
case unique :: Nil => unique
466-
case _ => fail(s"Unexpected multiple results for a companion symbol lookup for $original")
467-
}
468-
if (s != NoSymbol && s.owner == expectedOwner) res = s
469-
else ctx = ctx.outer
470-
}
471-
res
472-
}).asInstanceOf[Tree]
473-
}
474-
475444
val comp = tpe.typeSymbol.companion
476445
if (comp.isModule) comp
477-
else patchedCompanionRef(tpe).symbol
446+
else {
447+
// Borrowed from Magnolia: https://github.com/propensive/magnolia/blob/f21f2aabb49e43b372240e98ec77981662cc570c/core/shared/src/main/scala/magnolia.scala#L123-L155
448+
val ownerChainOf: Symbol => Iterator[Symbol] =
449+
s => Iterator.iterate(s)(_.owner).takeWhile(x => x != null && x != NoSymbol).toVector.reverseIterator
450+
val path = ownerChainOf(tpe.typeSymbol)
451+
.zipAll(ownerChainOf(enclosingOwner), NoSymbol, NoSymbol)
452+
.dropWhile { case (x, y) => x == y }
453+
.takeWhile(_._1 != NoSymbol)
454+
.map(_._1.name.toTermName)
455+
if (path.isEmpty) fail(s"Cannot find a companion for $tpe")
456+
else c.typecheck(path.foldLeft[Tree](Ident(path.next()))(Select(_, _)), silent = true).symbol
457+
}
478458
}
479459

480460
def isContainer(tpe: Type): Boolean =
@@ -563,19 +543,11 @@ object JsonCodecMaker {
563543
if (values.isEmpty) {
564544
val comp = companion(tpe)
565545
values =
566-
if (comp != NoSymbol) {
567-
comp.typeSignature.members.collect { case m: MethodSymbol if m.isGetter && m.returnType.dealias =:= tpe =>
568-
val name = decodeName(m)
569-
val transformedName = javaEnumValueNameMapper(name)
570-
EnumValueInfo(q"$comp.${TermName(name)}", transformedName, name != transformedName)
571-
}.toSeq
572-
} else { // FIXME: Scala 2.11.x returns empty set of subclasses for Java enums
573-
eval[Seq[(String, String)]](q"$tpe.values.map[(String, String)](e => (e.getClass.getName, e.name))").map {
574-
case (className, name) =>
575-
val transformedName = javaEnumValueNameMapper(name)
576-
EnumValueInfo(q"$className.${TermName(name)}", transformedName, name != transformedName)
577-
}
578-
}
546+
comp.typeSignature.members.collect { case m: MethodSymbol if m.isGetter && m.returnType.dealias =:= tpe =>
547+
val name = decodeName(m)
548+
val transformedName = javaEnumValueNameMapper(name)
549+
EnumValueInfo(q"$comp.${TermName(name)}", transformedName, name != transformedName)
550+
}.toSeq
579551
}
580552
val nameCollisions = duplicated(values.map(_.name))
581553
if (nameCollisions.nonEmpty) {

0 commit comments

Comments
 (0)