Skip to content
This repository was archived by the owner on Jun 4, 2024. It is now read-only.

Better [un]used dependency checking #156

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
43 changes: 43 additions & 0 deletions src/main/scala/higherkindness/rules_scala/used_deps/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
load("//rules:scalafmt.bzl", "scala_format_test")
load("//rules:scala.bzl", "scala_library")

scala_library(
name = "used_deps",
srcs = glob(["**/*.scala"]),
scala = "//external:scala_annex_scala",
visibility = ["//visibility:public"],
deps = [
"@scala_annex_scala_2_12_scala_compiler//jar",
"@scala_annex_scala_2_12_scala_reflect//jar",
],
resource_jars = [
":resources",
],
)

_gen_plugin_xml_cmd = """
cat > $@ << EOF
<plugin>
<name>name</name>
<classname>higherkindness.rules_scala.used_deps.UsedDepsPlugin</classname>
</plugin>
"""

genrule(
name = "gen-scalac-plugin.xml",
outs = ["scalac-plugin.xml"],
cmd = _gen_plugin_xml_cmd,
)

java_binary(
name = "resources",
classpath_resources = [
":gen-scalac-plugin.xml",
],
create_executable = False,
)

scala_format_test(
name = "format",
srcs = glob(["**/*.scala"]),
)
94 changes: 94 additions & 0 deletions src/main/scala/higherkindness/rules_scala/used_deps/plugin.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package higherkindness.rules_scala
package used_deps

import scala.reflect.io.AbstractFile
import scala.reflect.io.NoAbstractFile
import scala.reflect.internal.SymbolTable
import scala.tools.nsc.Global
import scala.tools.nsc.Phase
import scala.tools.nsc.plugins.Plugin
import scala.tools.nsc.plugins.PluginComponent
import scala.util.matching.Regex

import java.io.File

final class UsedDepsPlugin(override val global: Global) extends Plugin {

override val name: String = "used-deps"
override val description: String = "tracks used dependencies"
override val components: List[PluginComponent] = usedDeps :: Nil

private object usedDeps extends PluginComponent {
override val global: Global = UsedDepsPlugin.this.global
override val phaseName: String = "unused-deps"
override val runsAfter: List[String] = global.refChecks.phaseName :: Nil

private[used_deps] var outputFile: Option[File] = None

override def newPhase(prev: Phase): Phase =
new StdPhase(prev) with UsedDepsPhase[global.type] {
val global = usedDeps.this.global
}
}

override val optionsHelp: Option[String] = Some(
" -P:unused-deps:out=<path> Path to write output file (default: stdout)"
)

private implicit final class RegexInterpolator(sc: StringContext) {
def r = new Regex(sc.parts.mkString, sc.parts.tail.map(_ => "x"): _*)
}

override def init(options: List[String], error: String => Unit): Boolean = {
options.foreach {
case r"out=(.*)$out" =>
usedDeps.outputFile = Some(new File(out))
case arg =>
error(s"Unexpected argument: $arg")
}
true
}

{
hijackField(classOf[SymbolTable], "traceSymbolActivity", true)
}

if (global.traceSymbolActivity != true)
sys.error("unable to force tracing of symbol activity")

private[this] def hijackField[T](clazz: Class[_], name: String, newValue: T): T = {
val field = clazz.getDeclaredField(name)
field.setAccessible(true)
val oldValue = field.get(global).asInstanceOf[T]
field.set(global, newValue)
oldValue
}

}


trait UsedDepsPhase[G <: Global] {
val global: G
import global._

def apply(unit: CompilationUnit): Unit = {
val map = traceSymbols.allSymbols.values
.filter(_.associatedFile != NoAbstractFile)
.filter(_.associatedFile.file != null)
.foldLeft(Map.empty[File, List[Symbol]]) { (acc, sym) =>
val k = sym.associatedFile.file
acc.get(k) match {
case Some(vv) => acc + ((k, sym :: vv))
case None => acc + ((k, sym :: Nil))
}
}

map.foreach { case (k, v) =>
println(k)
v.foreach(sym => println(" " + sym))
}

()
}

}
47 changes: 47 additions & 0 deletions src/test/scala/higherkindness/rules_scala/used_deps/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
load("//rules:scalafmt.bzl", "scala_format_test")
load("//rules:scala.bzl", "scala_library")

scala_library(
name = "LibA1",
srcs = [
"LibA1.scala",
],
scala = "//external:scala_annex_scala",
plugins = [
"//src/main/scala/higherkindness/rules_scala/used_deps",
],
visibility = ["//visibility:public"],
)

scala_library(
name = "LibA2",
srcs = [
"LibA2.scala",
],
scala = "//external:scala_annex_scala",
plugins = [
"//src/main/scala/higherkindness/rules_scala/used_deps",
],
visibility = ["//visibility:public"],
)

scala_library(
name = "LibB",
srcs = [
"LibB.scala",
],
deps = [
":LibA1",
":LibA2",
],
scala = "//external:scala_annex_scala",
plugins = [
"//src/main/scala/higherkindness/rules_scala/used_deps",
],
visibility = ["//visibility:public"],
)

scala_format_test(
name = "format",
srcs = glob(["**/*.scala"]),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package higherkindness.rules_scala
package used_deps

trait LibA1
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package higherkindness.rules_scala
package used_deps

trait LibA2
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package higherkindness.rules_scala
package used_deps

trait LibB extends LibA1 with LibA2
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package higherkindness.rules_scala
package test1

class Test1 {

}