vendredi 30 septembre 2016

Scala Reflection Synthetics + Wart Remover

I'm trying to fix this Github issue: http://ift.tt/2dwGj3s

Essentially, WartRemover provides a Wart that is supposed to check for usages of asInstanceOf. The implementation (link) has allowances to skip things such as compiler generated classes, which it doesn't make sense to check. However, when I use implicit TypeTags, the generated code still seems to be inspected - and fails the checks.

This is my sample code:

import scala.reflect.runtime.universe.TypeTag

class GoodAsInstanceOf {
  def takesTypeTag[A : TypeTag](a: A): String = {
    val tt = implicitly[TypeTag[A]]
    s"The tt of A is $tt"
  }

  def exerciseIt(): String = {
    takesTypeTag("Hello")
  }
}

I'm using scala 2.10.6.

The -Xtyper output is:

// GoodAsInstanceOf.scala
package <empty> {
  import scala.reflect.runtime.`package`.universe.TypeTag;
  class GoodAsInstanceOf extends scala.AnyRef {
    def <init>(): GoodAsInstanceOf = {
      GoodAsInstanceOf.super.<init>();
      ()
    };
    def takesTypeTag[A >: Nothing <: Any](a: A)(implicit evidence$1: reflect.runtime.universe.TypeTag[A]): String = {
      val tt: reflect.runtime.universe.TypeTag[A] = scala.this.Predef.implicitly[reflect.runtime.universe.TypeTag[A]](evidence$1);
      scala.StringContext.apply("The tt of A is ", "").s(tt)
    };
    def exerciseIt(): String = GoodAsInstanceOf.this.takesTypeTag[String]("Hello")({
      val $u: reflect.runtime.universe.type = scala.reflect.runtime.`package`.universe;
      val $m: $u.Mirror = scala.reflect.runtime.`package`.universe.runtimeMirror(classOf[GoodAsInstanceOf].getClassLoader());
      $u.TypeTag.apply[String]($m, {
        final class $typecreator1 extends TypeCreator {
          def <init>(): $typecreator1 = {
            $typecreator1.super.<init>();
            ()
          };
          def apply[U >: Nothing <: scala.reflect.api.Universe with Singleton]($m$untyped: scala.reflect.api.Mirror[U]): U#Type = {
            val $u: U = $m$untyped.universe;
            val $m: $u.Mirror = $m$untyped.asInstanceOf[$u.Mirror];
            $m.staticClass("java.lang.String").asType.toTypeConstructor
          }
        };
        new $typecreator1()
      })
    })
  }
}

Unfortunately, there is a false positive on the call to takesTypeTag - which I'm trying to rectify.

I would imagine that calling isSynthetic on the relevant symbols within the generated code should return true - particularly the ClassDef $typecreator1, or the code it contains. This could then be used to skip those parts of the tree - which is how the default Wart expects it to behave:

// Ignore usage in synthetic classes
case ClassDef(_, _, _, _) if synthetic => 

However - isSynthetic always returns false within this code. (I am testing using a modified copy of the Wart class above, and producing warnings containing debug information as the tree is traversed. I've tried checking the generated Block and apply method also).

Am I misunderstanding how isSynthetic works? Is there a better way to do this?





Aucun commentaire:

Enregistrer un commentaire