Skip to content

Commit

Permalink
Fix #72.
Browse files Browse the repository at this point in the history
This allows users to add custom rewrites to the compiler classpath and
classload them by fully qualified name.
  • Loading branch information
olafurpg committed Feb 23, 2017
1 parent 0595ebb commit d0c05db
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import scala.util.matching.Regex
import scalafix.Failure.UnknownRewrite
import scalafix.rewrite.ScalafixRewrite
import scalafix.rewrite.ScalafixRewrites
import scalafix.util.ClassloadObject
import scalafix.util.TreePatch._

import metaconfig.Reader
Expand All @@ -26,7 +27,13 @@ trait ScalafixMetaconfigReaders {
}

implicit lazy val rewriteReader: Reader[ScalafixRewrite] =
ReaderUtil.fromMap(ScalafixRewrites.name2rewrite)
Reader.instance[ScalafixRewrite] {
case fqn: String if fqn.startsWith("_root_.") =>
val suffix = if (fqn.endsWith("$")) "" else "$"
ClassloadObject[ScalafixRewrite](fqn.stripPrefix("_root_.") + suffix)
case els =>
ReaderUtil.fromMap(ScalafixRewrites.name2rewrite).read(els)
}

implicit val RegexReader: Reader[Regex] = Reader.instance[Regex] {
case str: String => Right(FilterMatcher.mkRegexp(List(str)))
Expand Down
48 changes: 48 additions & 0 deletions core/src/main/scala/scalafix/util/ClassloadObject.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package scalafix.util

import scala.collection.immutable.Seq
import scala.reflect.ClassTag
import scala.util.Failure
import scala.util.Success
import scala.util.Try

import java.lang.reflect.InvocationTargetException

// Helper to classload object or no argument class.
class ClassloadObject[T](classLoader: ClassLoader)(implicit ev: ClassTag[T]) {
private val t = ev.runtimeClass

private def getClassFor(fqcn: String): Try[Class[_ <: T]] =
Try[Class[_ <: T]]({
val c =
Class.forName(fqcn, false, classLoader).asInstanceOf[Class[_ <: T]]
if (t.isAssignableFrom(c)) c
else throw new ClassCastException(s"$t is not assignable from $c")
})

private def createInstanceFor(clazz: Class[_]): Try[T] =
Try {
val constructor = clazz.getDeclaredConstructor()
constructor.setAccessible(true)
val obj = constructor.newInstance()
if (t.isInstance(obj)) obj.asInstanceOf[T]
else
throw new ClassCastException(
s"${clazz.getName} is not a subtype of $t")
} recover {
case i: InvocationTargetException if i.getTargetException ne null
throw i.getTargetException
}

def createInstanceFor(fqcn: String): Try[T] =
getClassFor(fqcn).flatMap(c => createInstanceFor(c))
}

object ClassloadObject {
def apply[T: ClassTag](fqn: String): Either[Throwable, T] =
new ClassloadObject(this.getClass.getClassLoader)
.createInstanceFor(fqn) match {
case Success(e) => Right(e)
case Failure(e) => Left(e)
}
}
9 changes: 9 additions & 0 deletions scalafix-nsc/src/test/resources/checkSyntax/FqnRewrite.source
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
rewrites = [
_root_.scalafix.test.FqnRewrite
]
<<< NOWRAP add import
import scala._
object FqnMe
>>>
import scala.meta._
object FqnMe
13 changes: 13 additions & 0 deletions scalafix-nsc/src/test/scala/scalafix/test/FqnRewrite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package scalafix.test

import scala.meta._
import scala.collection.immutable.Seq
import scalafix.rewrite.Rewrite
import scalafix.rewrite.RewriteCtx
import scalafix.util.Patch
import scalafix.util.TreePatch.AddGlobalImport

case object FqnRewrite extends Rewrite[Any] {
override def rewrite[B <: Any](ctx: RewriteCtx[B]): Seq[Patch] =
Seq(AddGlobalImport(importer"scala.meta._"))
}

1 comment on commit d0c05db

@xeno-by
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is super exciting!

Please sign in to comment.