mirror of https://github.com/sbt/sbt.git
Merge pull request #2583 from eed3si9n/wip/2523
Disable scripted test for source-dependencies/as-seen-from-b
This commit is contained in:
commit
74d8a574d1
|
|
@ -51,6 +51,22 @@ abstract class Compat {
|
||||||
def transformedType(tpe: Type): Type = tpe
|
def transformedType(tpe: Type): Type = tpe
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traverses given type and collects result of applying a partial function `pf`.
|
||||||
|
*
|
||||||
|
* NOTE: This class exists in Scala 2.10 as CollectTypeCollector but does not in earlier
|
||||||
|
* versions (like 2.9) of Scala compiler that incremental cmpiler supports so we had to
|
||||||
|
* reimplement that class here.
|
||||||
|
*/
|
||||||
|
class CollectTypeTraverser[T](pf: PartialFunction[Type, T]) extends TypeTraverser {
|
||||||
|
var collected: List[T] = Nil
|
||||||
|
def traverse(tpe: Type): Unit = {
|
||||||
|
if (pf.isDefinedAt(tpe))
|
||||||
|
collected = pf(tpe) :: collected
|
||||||
|
mapOver(tpe)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private[this] final class MiscCompat {
|
private[this] final class MiscCompat {
|
||||||
// in 2.9, nme.LOCALCHILD was renamed to tpnme.LOCAL_CHILD
|
// in 2.9, nme.LOCALCHILD was renamed to tpnme.LOCAL_CHILD
|
||||||
def tpnme = nme
|
def tpnme = nme
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ object Dependency {
|
||||||
* where it originates from. The Symbol->Classfile mapping is implemented by
|
* where it originates from. The Symbol->Classfile mapping is implemented by
|
||||||
* LocateClassFile that we inherit from.
|
* LocateClassFile that we inherit from.
|
||||||
*/
|
*/
|
||||||
final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
|
final class Dependency(val global: CallbackGlobal) extends LocateClassFile with GlobalHelpers {
|
||||||
import global._
|
import global._
|
||||||
|
|
||||||
def newPhase(prev: Phase): Phase = new DependencyPhase(prev)
|
def newPhase(prev: Phase): Phase = new DependencyPhase(prev)
|
||||||
|
|
@ -79,6 +79,12 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
|
||||||
private class ExtractDependenciesTraverser extends Traverser {
|
private class ExtractDependenciesTraverser extends Traverser {
|
||||||
private val _dependencies = collection.mutable.HashSet.empty[Symbol]
|
private val _dependencies = collection.mutable.HashSet.empty[Symbol]
|
||||||
protected def addDependency(dep: Symbol): Unit = if (dep ne NoSymbol) _dependencies += dep
|
protected def addDependency(dep: Symbol): Unit = if (dep ne NoSymbol) _dependencies += dep
|
||||||
|
protected def addTreeDependency(tree: Tree): Unit = {
|
||||||
|
addDependency(tree.symbol)
|
||||||
|
if (tree.tpe != null)
|
||||||
|
symbolsInType(tree.tpe).foreach(addDependency)
|
||||||
|
()
|
||||||
|
}
|
||||||
def dependencies: Iterator[Symbol] = _dependencies.iterator
|
def dependencies: Iterator[Symbol] = _dependencies.iterator
|
||||||
def topLevelDependencies: Iterator[Symbol] = _dependencies.map(enclosingTopLevelClass).iterator
|
def topLevelDependencies: Iterator[Symbol] = _dependencies.map(enclosingTopLevelClass).iterator
|
||||||
|
|
||||||
|
|
@ -118,11 +124,11 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
|
||||||
* this looks fishy, see this thread:
|
* this looks fishy, see this thread:
|
||||||
* https://groups.google.com/d/topic/scala-internals/Ms9WUAtokLo/discussion
|
* https://groups.google.com/d/topic/scala-internals/Ms9WUAtokLo/discussion
|
||||||
*/
|
*/
|
||||||
case id: Ident => addDependency(id.symbol)
|
case id: Ident => addTreeDependency(id)
|
||||||
case sel @ Select(qual, _) =>
|
case sel @ Select(qual, _) =>
|
||||||
traverse(qual); addDependency(sel.symbol)
|
traverse(qual); addTreeDependency(sel)
|
||||||
case sel @ SelectFromTypeTree(qual, _) =>
|
case sel @ SelectFromTypeTree(qual, _) =>
|
||||||
traverse(qual); addDependency(sel.symbol)
|
traverse(qual); addTreeDependency(sel)
|
||||||
|
|
||||||
case Template(parents, self, body) =>
|
case Template(parents, self, body) =>
|
||||||
// use typeSymbol to dealias type aliases -- we want to track the dependency on the real class in the alias's RHS
|
// use typeSymbol to dealias type aliases -- we want to track the dependency on the real class in the alias's RHS
|
||||||
|
|
@ -151,32 +157,6 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
|
||||||
super.traverse(m)
|
super.traverse(m)
|
||||||
case other => super.traverse(other)
|
case other => super.traverse(other)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def symbolsInType(tp: Type): Set[Symbol] = {
|
|
||||||
val typeSymbolCollector =
|
|
||||||
new CollectTypeTraverser({
|
|
||||||
case tpe if (tpe != null) && !tpe.typeSymbolDirect.isPackage => tpe.typeSymbolDirect
|
|
||||||
})
|
|
||||||
|
|
||||||
typeSymbolCollector.traverse(tp)
|
|
||||||
typeSymbolCollector.collected.toSet
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Traverses given type and collects result of applying a partial function `pf`.
|
|
||||||
*
|
|
||||||
* NOTE: This class exists in Scala 2.10 as CollectTypeCollector but does not in earlier
|
|
||||||
* versions (like 2.9) of Scala compiler that incremental cmpiler supports so we had to
|
|
||||||
* reimplement that class here.
|
|
||||||
*/
|
|
||||||
private final class CollectTypeTraverser[T](pf: PartialFunction[Type, T]) extends TypeTraverser {
|
|
||||||
var collected: List[T] = Nil
|
|
||||||
def traverse(tpe: Type): Unit = {
|
|
||||||
if (pf.isDefinedAt(tpe))
|
|
||||||
collected = pf(tpe) :: collected
|
|
||||||
mapOver(tpe)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Copied straight from Scala 2.10 as it does not exist in Scala 2.9 compiler */
|
/** Copied straight from Scala 2.10 as it does not exist in Scala 2.9 compiler */
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ import scala.tools.nsc._
|
||||||
*
|
*
|
||||||
* Extracts simple (unqualified) names mentioned in given in non-definition position by collecting
|
* Extracts simple (unqualified) names mentioned in given in non-definition position by collecting
|
||||||
* all symbols associated with non-definition trees and extracting names from all collected symbols.
|
* all symbols associated with non-definition trees and extracting names from all collected symbols.
|
||||||
|
* Also extract the names of the types of non-definition trees (see source-dependencies/types-in-used-names
|
||||||
|
* for an example where this is required).
|
||||||
*
|
*
|
||||||
* If given symbol is mentioned both in definition and in non-definition position (e.g. in member
|
* If given symbol is mentioned both in definition and in non-definition position (e.g. in member
|
||||||
* selection) then that symbol is collected. It means that names of symbols defined and used in the
|
* selection) then that symbol is collected. It means that names of symbols defined and used in the
|
||||||
|
|
@ -38,7 +40,7 @@ import scala.tools.nsc._
|
||||||
* The tree walking algorithm walks into TypeTree.original explicitly.
|
* The tree walking algorithm walks into TypeTree.original explicitly.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) extends Compat {
|
class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) extends Compat with GlobalHelpers {
|
||||||
import global._
|
import global._
|
||||||
|
|
||||||
@inline def debug(msg: => String) = if (settings.verbose.value) inform(msg)
|
@inline def debug(msg: => String) = if (settings.verbose.value) inform(msg)
|
||||||
|
|
@ -61,10 +63,12 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) ext
|
||||||
*/
|
*/
|
||||||
val inspectedOriginalTrees = collection.mutable.Set.empty[Tree]
|
val inspectedOriginalTrees = collection.mutable.Set.empty[Tree]
|
||||||
|
|
||||||
def addSymbol(symbol: Symbol): Unit = {
|
def addSymbol(symbol: Symbol): Unit =
|
||||||
val symbolNameAsString = symbol.name.decode.trim
|
if (eligibleAsUsedName(symbol)) {
|
||||||
namesBuffer += symbolNameAsString
|
val symbolNameAsString = symbol.name.decode.trim
|
||||||
}
|
namesBuffer += symbolNameAsString
|
||||||
|
()
|
||||||
|
}
|
||||||
|
|
||||||
def handleTreeNode(node: Tree): Unit = {
|
def handleTreeNode(node: Tree): Unit = {
|
||||||
def handleMacroExpansion(original: Tree): Unit = {
|
def handleMacroExpansion(original: Tree): Unit = {
|
||||||
|
|
@ -91,8 +95,10 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) ext
|
||||||
// not what we need
|
// not what we need
|
||||||
case t: TypeTree if t.original != null =>
|
case t: TypeTree if t.original != null =>
|
||||||
t.original.foreach(handleTreeNode)
|
t.original.foreach(handleTreeNode)
|
||||||
case t if t.hasSymbol && eligibleAsUsedName(t.symbol) =>
|
case t if t.hasSymbol =>
|
||||||
addSymbol(t.symbol)
|
addSymbol(t.symbol)
|
||||||
|
if (t.tpe != null)
|
||||||
|
symbolsInType(t.tpe).foreach(addSymbol)
|
||||||
case _ => ()
|
case _ => ()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package xsbt
|
||||||
|
|
||||||
|
import scala.tools.nsc.Global
|
||||||
|
|
||||||
|
trait GlobalHelpers extends Compat {
|
||||||
|
val global: CallbackGlobal
|
||||||
|
import global.{ Tree, Type, Symbol, TypeTraverser }
|
||||||
|
|
||||||
|
def symbolsInType(tp: Type): Set[Symbol] = {
|
||||||
|
val typeSymbolCollector =
|
||||||
|
new CollectTypeTraverser({
|
||||||
|
case tpe if (tpe != null) && !tpe.typeSymbolDirect.isPackage => tpe.typeSymbolDirect
|
||||||
|
})
|
||||||
|
|
||||||
|
typeSymbolCollector.traverse(tp)
|
||||||
|
typeSymbolCollector.collected.toSet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,8 +17,9 @@ class ExtractUsedNamesSpecification extends Specification {
|
||||||
* definition.
|
* definition.
|
||||||
*/
|
*/
|
||||||
private val standardNames = Set(
|
private val standardNames = Set(
|
||||||
// AnyRef is added as default parent of a class
|
"scala",
|
||||||
"scala", "AnyRef",
|
// The default parent of a class is "AnyRef" which is an alias for "Object"
|
||||||
|
"AnyRef", "Object",
|
||||||
// class receives a default constructor which is internally called "<init>"
|
// class receives a default constructor which is internally called "<init>"
|
||||||
"<init>")
|
"<init>")
|
||||||
|
|
||||||
|
|
@ -69,7 +70,45 @@ class ExtractUsedNamesSpecification extends Specification {
|
||||||
|}""".stripMargin
|
|}""".stripMargin
|
||||||
val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true)
|
val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true)
|
||||||
val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcA, srcB)
|
val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcA, srcB)
|
||||||
val expectedNames = standardNames ++ Set("A", "a", "B", "=")
|
val expectedNames = standardNames ++ Set("A", "a", "B", "=", "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", "B", "x", "y", "z",
|
||||||
|
"Predef", "???", "Nothing",
|
||||||
|
"lista", "package", "List", "A",
|
||||||
|
"at", "T",
|
||||||
|
"as", "S",
|
||||||
|
"foo", "M", "N",
|
||||||
|
"bar", "Param", "P1", "P0")
|
||||||
usedNames === expectedNames
|
usedNames === expectedNames
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
abstract class A {
|
||||||
|
type T
|
||||||
|
object X {
|
||||||
|
def foo(x: T): T = x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
class B extends A {
|
||||||
|
type T = Int
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
object C extends B
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
object D {
|
||||||
|
C.X.foo(12)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
class B extends A {
|
||||||
|
type T = String
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
> compile
|
||||||
|
$ copy-file changes/B2.scala B.scala
|
||||||
|
-> compile
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
lazy val root = (project in file(".")).
|
||||||
|
settings(
|
||||||
|
scalaVersion := "2.11.8"
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
abstract class A {
|
||||||
|
type T <: S
|
||||||
|
type S
|
||||||
|
object X {
|
||||||
|
def foo: T = null.asInstanceOf[T]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
class B extends A {
|
||||||
|
type S <: Int
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
class B extends A {
|
||||||
|
type S <: String
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
object C extends B
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
object D {
|
||||||
|
val x: Int = C.X.foo
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
$ copy-file changes/A1.scala A.scala
|
||||||
|
|
||||||
|
> compile
|
||||||
|
|
||||||
|
$ copy-file changes/B1.scala B.scala
|
||||||
|
|
||||||
|
> compile
|
||||||
|
|
||||||
|
|
||||||
|
$ copy-file changes/C1.scala C.scala
|
||||||
|
|
||||||
|
> compile
|
||||||
|
|
||||||
|
|
||||||
|
$ copy-file changes/D1.scala D.scala
|
||||||
|
|
||||||
|
> compile
|
||||||
|
|
||||||
|
$ copy-file changes/B2.scala B.scala
|
||||||
|
$ sleep 1000
|
||||||
|
|
||||||
|
-> compile
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
class A
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
class B extends A
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
object C {
|
||||||
|
val listb: List[B] = List(new B)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
object D {
|
||||||
|
val lista: List[A] = C.listb
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
class B
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
> compile
|
||||||
|
$ copy-file changes/B2.scala B.scala
|
||||||
|
# Compilation of D.scala should fail because B is no longer a subtype of A
|
||||||
|
-> compile
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
class A {
|
||||||
|
type T <: S
|
||||||
|
type S <: Int
|
||||||
|
def foo: T = null.asInstanceOf[T]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
object B {
|
||||||
|
val x: Int = (new A).foo
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
class A {
|
||||||
|
type T <: S
|
||||||
|
type S <: String
|
||||||
|
def foo: T = null.asInstanceOf[T]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
> compile
|
||||||
|
$ copy-file changes/A2.scala A.scala
|
||||||
|
# Same workaround as #2565
|
||||||
|
# Compilation of B.scala should fail because A#S is no longer a subtype of Int
|
||||||
|
-> compile
|
||||||
Loading…
Reference in New Issue