/** Adapted from https://github.com/sbt/sbt/blob/0.13/compile/interface/src/test/scala/xsbt/ExtractUsedNamesSpecification.scala */ package xsbt import org.junit.runner.RunWith import xsbti.api.ClassLike import xsbti.api.Def import xsbti.api.Package import xsbt.api.SameAPI import org.junit.runners.JUnit4 import org.specs2.mutable.Specification @RunWith(classOf[JUnit4]) class ExtractUsedNamesSpecification extends Specification { /** * Standard names that appear in every compilation unit that has any class * definition. */ private val standardNames = Set( // All class extend Object "Object", // All class have a default constructor called "", // the return type of the default constructor is Unit "Unit" ) "imported name" in { val src = """ |package a { class A } |package b { | import a.{A => A2} |}""".stripMargin val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) val usedNames = compilerForTesting.extractUsedNamesFromSrc(src) val expectedNames = standardNames ++ Set("a", "A", "A2", "b") usedNames === expectedNames } // test covers https://github.com/gkossakowski/sbt/issues/6 "names in type tree" in { val srcA = """| |package a { | class A { | class C { class D } | } | class B[T] | class BB |}""".stripMargin val srcB = """| |package b { | abstract class X { | def foo: a.A#C#D | def bar: a.B[a.BB] | } |}""".stripMargin val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcA, srcB) // DOTTY: unlike the scalac sbt phase, this does not contain "X", I believe this is safe // TODO: report issue against sbt suggesting that they do the same val expectedNames = standardNames ++ Set("a", "A", "B", "C", "D", "b", "BB") usedNames === expectedNames } // test for https://github.com/gkossakowski/sbt/issues/5 "symbolic names" in { val srcA = """| |class A { | def `=`: Int = 3 |}""".stripMargin val srcB = """| |class B { | def foo(a: A) = a.`=` |}""".stripMargin val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcA, srcB) // DOTTY TODO: "Int" is not actually used, but we collect it because // it's the inferred return type so it appears in a TypeTree // We could avoid this by checking if the untyped tree has a return type // but is it worth it? Revisit this after https://github.com/sbt/sbt/issues/1104 // has landed. val expectedNames = standardNames ++ Set("A", "a", "$eq", "Int") usedNames === expectedNames } "extract names in the types of trees" in { val src1 = """|class X0 |class X1 extends X0 |class Y |class A { | type T >: X1 <: X0 |} |class M |class N |class P0 |class P1 extends P0 |object B { | type S = Y | val lista: List[A] = ??? | val at: A#T = ??? | val as: S = ??? | def foo(m: M): N = ??? | def bar[Param >: P1 <: P0](p: Param): Param = ??? |}""".stripMargin val src2 = """|object Test { | val x = B.lista | val y = B.at | val z = B.as | B.foo(???) | B.bar(???) |}""".stripMargin val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) val usedNames = compilerForTesting.extractUsedNamesFromSrc(src1, src2) val expectedNames = standardNames ++ Set("Test", "Test$", "B", "B$", "Predef", "Predef$", "$qmark$qmark$qmark", "Nothing", "lista", "List", "A", "at", "T", "X1", "X0", "as", "S", "Y", "foo", "M", "N", "bar", "P1", "P0") usedNames === expectedNames } // test for https://github.com/gkossakowski/sbt/issues/3 "used names from the same compilation unit" in { val src = "class A { def foo: Int = 0; def bar: Int = foo }" val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) val usedNames = compilerForTesting.extractUsedNamesFromSrc(src) val expectedNames = standardNames ++ Set("A", "foo", "Int") usedNames === expectedNames } // pending test for https://issues.scala-lang.org/browse/SI-7173 "names of constants" in { val src = "class A { final val foo = 12; def bar: Int = foo }" val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) val usedNames = compilerForTesting.extractUsedNamesFromSrc(src) val expectedNames = standardNames ++ Set("A", "foo", "Int") usedNames === expectedNames } // pending test for https://github.com/gkossakowski/sbt/issues/4 // TODO: we should fix it by having special treatment of `selectDynamic` and `applyDynamic` calls "names from method calls on Dynamic" in { val srcA = """|import scala.language.dynamics |class A extends Dynamic { | def selectDynamic(name: String): Int = name.length |}""".stripMargin val srcB = "class B { def foo(a: A): Int = a.bla }" val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcA, srcB) val expectedNames = standardNames ++ Set("B", "A", "a", "Int", "selectDynamic", "bla") usedNames === expectedNames }.pendingUntilFixed("Call to Dynamic is desugared in type checker so Select nodes is turned into string literal.") }