Merge pull request #2583 from eed3si9n/wip/2523

Disable scripted test for source-dependencies/as-seen-from-b
This commit is contained in:
eugene yokota 2016-05-04 10:20:50 -04:00
commit 74d8a574d1
28 changed files with 191 additions and 39 deletions

View File

@ -51,6 +51,22 @@ abstract class Compat {
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 {
// in 2.9, nme.LOCALCHILD was renamed to tpnme.LOCAL_CHILD
def tpnme = nme

View File

@ -29,7 +29,7 @@ object Dependency {
* where it originates from. The Symbol->Classfile mapping is implemented by
* 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._
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 val _dependencies = collection.mutable.HashSet.empty[Symbol]
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 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:
* 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, _) =>
traverse(qual); addDependency(sel.symbol)
traverse(qual); addTreeDependency(sel)
case sel @ SelectFromTypeTree(qual, _) =>
traverse(qual); addDependency(sel.symbol)
traverse(qual); addTreeDependency(sel)
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
@ -151,32 +157,6 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
super.traverse(m)
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 */

View File

@ -7,6 +7,8 @@ import scala.tools.nsc._
*
* 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.
* 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
* 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.
*
*/
class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) extends Compat {
class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) extends Compat with GlobalHelpers {
import global._
@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]
def addSymbol(symbol: Symbol): Unit = {
val symbolNameAsString = symbol.name.decode.trim
namesBuffer += symbolNameAsString
}
def addSymbol(symbol: Symbol): Unit =
if (eligibleAsUsedName(symbol)) {
val symbolNameAsString = symbol.name.decode.trim
namesBuffer += symbolNameAsString
()
}
def handleTreeNode(node: Tree): Unit = {
def handleMacroExpansion(original: Tree): Unit = {
@ -91,8 +95,10 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) ext
// not what we need
case t: TypeTree if t.original != null =>
t.original.foreach(handleTreeNode)
case t if t.hasSymbol && eligibleAsUsedName(t.symbol) =>
case t if t.hasSymbol =>
addSymbol(t.symbol)
if (t.tpe != null)
symbolsInType(t.tpe).foreach(addSymbol)
case _ => ()
}

View File

@ -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
}
}

View File

@ -17,8 +17,9 @@ class ExtractUsedNamesSpecification extends Specification {
* definition.
*/
private val standardNames = Set(
// AnyRef is added as default parent of a class
"scala", "AnyRef",
"scala",
// 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>"
"<init>")
@ -69,7 +70,45 @@ class ExtractUsedNamesSpecification extends Specification {
|}""".stripMargin
val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true)
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
}

View File

@ -0,0 +1,6 @@
abstract class A {
type T
object X {
def foo(x: T): T = x
}
}

View File

@ -0,0 +1,3 @@
class B extends A {
type T = Int
}

View File

@ -0,0 +1 @@
object C extends B

View File

@ -0,0 +1,3 @@
object D {
C.X.foo(12)
}

View File

@ -0,0 +1,3 @@
class B extends A {
type T = String
}

View File

@ -0,0 +1,3 @@
> compile
$ copy-file changes/B2.scala B.scala
-> compile

View File

@ -0,0 +1,4 @@
lazy val root = (project in file(".")).
settings(
scalaVersion := "2.11.8"
)

View File

@ -0,0 +1,7 @@
abstract class A {
type T <: S
type S
object X {
def foo: T = null.asInstanceOf[T]
}
}

View File

@ -0,0 +1,3 @@
class B extends A {
type S <: Int
}

View File

@ -0,0 +1,3 @@
class B extends A {
type S <: String
}

View File

@ -0,0 +1 @@
object C extends B

View File

@ -0,0 +1,3 @@
object D {
val x: Int = C.X.foo
}

View File

@ -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

View File

@ -0,0 +1 @@
class A

View File

@ -0,0 +1 @@
class B extends A

View File

@ -0,0 +1,3 @@
object C {
val listb: List[B] = List(new B)
}

View File

@ -0,0 +1,3 @@
object D {
val lista: List[A] = C.listb
}

View File

@ -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

View File

@ -0,0 +1,5 @@
class A {
type T <: S
type S <: Int
def foo: T = null.asInstanceOf[T]
}

View File

@ -0,0 +1,3 @@
object B {
val x: Int = (new A).foo
}

View File

@ -0,0 +1,5 @@
class A {
type T <: S
type S <: String
def foo: T = null.asInstanceOf[T]
}

View File

@ -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