Skip to content

Commit dd4866b

Browse files
committed
Include used types in the set of used names
When `B2.scala` replaces `B.scala` in the new test `source-dependencies/types-in-used-names`, the name hash of `listb` does not change because the signature of `C.listb` is still `List[B]`, however users of `C.listb` have to be recompiled since the subtyping relationships of its type have changed. This commit does this by extending the definition of "used names" to also include the names of the types of trees, even if these types do not appear in the source like `List[B]` in `D.scala` (since `B` has been invalidated, this will force the recompilation of `D.scala`).
1 parent 2529bbd commit dd4866b

File tree

14 files changed

+149
-66
lines changed

14 files changed

+149
-66
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
class A
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
class B extends A
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object C {
2+
val listb: List[B] = List(new B)
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object D {
2+
val lista: List[A] = C.listb
3+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
class B
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
> compile
2+
$ copy-file changes/B2.scala B.scala
3+
# Compilation of D.scala should fail because B is no longer a subtype of A
4+
-> compile

internal/compiler-bridge/src-2.10/main/scala/xsbt/Compat.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,22 @@ abstract class Compat {
5151
def transformedType(tpe: Type): Type = tpe
5252
}
5353

54+
/**
55+
* Traverses given type and collects result of applying a partial function `pf`.
56+
*
57+
* NOTE: This class exists in Scala 2.10 as CollectTypeCollector but does not in earlier
58+
* versions (like 2.9) of Scala compiler that incremental cmpiler supports so we had to
59+
* reimplement that class here.
60+
*/
61+
class CollectTypeTraverser[T](pf: PartialFunction[Type, T]) extends TypeTraverser {
62+
var collected: List[T] = Nil
63+
def traverse(tpe: Type): Unit = {
64+
if (pf.isDefinedAt(tpe))
65+
collected = pf(tpe) :: collected
66+
mapOver(tpe)
67+
}
68+
}
69+
5470
private[this] final class MiscCompat {
5571
// in 2.9, nme.LOCALCHILD was renamed to tpnme.LOCAL_CHILD
5672
def tpnme = nme

internal/compiler-bridge/src-2.10/main/scala/xsbt/Dependency.scala

Lines changed: 11 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ object Dependency {
2929
* where it originates from. The Symbol->Classfile mapping is implemented by
3030
* LocateClassFile that we inherit from.
3131
*/
32-
final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
32+
final class Dependency(val global: CallbackGlobal) extends LocateClassFile with GlobalHelpers {
3333
import global._
3434

3535
def newPhase(prev: Phase): Phase = new DependencyPhase(prev)
@@ -50,7 +50,7 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
5050
unit.depends foreach processDependency(context = DependencyByMemberRef)
5151
inheritedDependencies.getOrElse(sourceFile, Nil: Iterable[Symbol]) foreach processDependency(context = DependencyByInheritance)
5252
}
53-
/**
53+
/*
5454
* Handles dependency on given symbol by trying to figure out if represents a term
5555
* that is coming from either source code (not necessarily compiled in this compilation
5656
* run) or from class file and calls respective callback method.
@@ -79,6 +79,12 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
7979
private class ExtractDependenciesTraverser extends Traverser {
8080
private val _dependencies = collection.mutable.HashSet.empty[Symbol]
8181
protected def addDependency(dep: Symbol): Unit = { if (dep ne NoSymbol) _dependencies += dep }
82+
protected def addTreeDependency(tree: Tree): Unit = {
83+
addDependency(tree.symbol)
84+
if (tree.tpe != null)
85+
symbolsInType(tree.tpe).foreach(addDependency)
86+
()
87+
}
8288
def dependencies: Iterator[Symbol] = _dependencies.iterator
8389
def topLevelDependencies: Iterator[Symbol] = _dependencies.map(enclosingTopLevelClass).iterator
8490

@@ -118,11 +124,11 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
118124
* this looks fishy, see this thread:
119125
* https://groups.google.com/d/topic/scala-internals/Ms9WUAtokLo/discussion
120126
*/
121-
case id: Ident => addDependency(id.symbol)
127+
case id: Ident => addTreeDependency(id)
122128
case sel @ Select(qual, _) =>
123-
traverse(qual); addDependency(sel.symbol)
129+
traverse(qual); addTreeDependency(sel)
124130
case sel @ SelectFromTypeTree(qual, _) =>
125-
traverse(qual); addDependency(sel.symbol)
131+
traverse(qual); addTreeDependency(sel)
126132

127133
case Template(parents, self, body) =>
128134
// use typeSymbol to dealias type aliases -- we want to track the dependency on the real class in the alias's RHS
@@ -149,32 +155,6 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
149155
case MacroExpansionOf(original) if inspectedOriginalTrees.add(original) => traverse(original)
150156
case other => super.traverse(other)
151157
}
152-
153-
private def symbolsInType(tp: Type): Set[Symbol] = {
154-
val typeSymbolCollector =
155-
new CollectTypeTraverser({
156-
case tpe if (tpe != null) && !tpe.typeSymbolDirect.isPackage => tpe.typeSymbolDirect
157-
})
158-
159-
typeSymbolCollector.traverse(tp)
160-
typeSymbolCollector.collected.toSet
161-
}
162-
}
163-
164-
/**
165-
* Traverses given type and collects result of applying a partial function `pf`.
166-
*
167-
* NOTE: This class exists in Scala 2.10 as CollectTypeCollector but does not in earlier
168-
* versions (like 2.9) of Scala compiler that incremental cmpiler supports so we had to
169-
* reimplement that class here.
170-
*/
171-
private final class CollectTypeTraverser[T](pf: PartialFunction[Type, T]) extends TypeTraverser {
172-
var collected: List[T] = Nil
173-
def traverse(tpe: Type): Unit = {
174-
if (pf.isDefinedAt(tpe))
175-
collected = pf(tpe) :: collected
176-
mapOver(tpe)
177-
}
178158
}
179159

180160
/** Copied straight from Scala 2.10 as it does not exist in Scala 2.9 compiler */

internal/compiler-bridge/src-2.10/main/scala/xsbt/ExtractUsedNames.scala

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import scala.tools.nsc._
77
*
88
* Extracts simple (unqualified) names mentioned in given in non-definition position by collecting
99
* all symbols associated with non-definition trees and extracting names from all collected symbols.
10+
* Also extract the names of the types of non-definition trees (see source-dependencies/types-in-used-names
11+
* for an example where this is required).
1012
*
1113
* If given symbol is mentioned both in definition and in non-definition position (e.g. in member
1214
* 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._
3840
* The tree walking algorithm walks into TypeTree.original explicitly.
3941
*
4042
*/
41-
class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) extends Compat {
43+
class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) extends Compat with GlobalHelpers {
4244
import global._
4345

4446
def extract(unit: CompilationUnit): Set[String] = {
@@ -59,11 +61,12 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) ext
5961
*/
6062
val inspectedOriginalTrees = collection.mutable.Set.empty[Tree]
6163

62-
def addSymbol(symbol: Symbol): Unit = {
63-
val symbolNameAsString = symbol.name.decode.trim
64-
namesBuffer += symbolNameAsString
65-
()
66-
}
64+
def addSymbol(symbol: Symbol): Unit =
65+
if (eligibleAsUsedName(symbol)) {
66+
val symbolNameAsString = symbol.name.decode.trim
67+
namesBuffer += symbolNameAsString
68+
()
69+
}
6770

6871
def handleTreeNode(node: Tree): Unit = {
6972
def handleMacroExpansion(original: Tree): Unit = {
@@ -92,8 +95,10 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) ext
9295
// not what we need
9396
case t: TypeTree if t.original != null =>
9497
t.original.foreach(handleTreeNode)
95-
case t if t.hasSymbol && eligibleAsUsedName(t.symbol) =>
98+
case t if t.hasSymbol =>
9699
addSymbol(t.symbol)
100+
if (t.tpe != null)
101+
symbolsInType(t.tpe).foreach(addSymbol)
97102
case _ => ()
98103
}
99104

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package xsbt
2+
3+
import scala.tools.nsc.Global
4+
5+
trait GlobalHelpers extends Compat {
6+
val global: CallbackGlobal
7+
import global.{ Tree, Type, Symbol, TypeTraverser }
8+
9+
def symbolsInType(tp: Type): Set[Symbol] = {
10+
val typeSymbolCollector =
11+
new CollectTypeTraverser({
12+
case tpe if (tpe != null) && !tpe.typeSymbolDirect.hasPackageFlag => tpe.typeSymbolDirect
13+
})
14+
15+
typeSymbolCollector.traverse(tp)
16+
typeSymbolCollector.collected.toSet
17+
}
18+
}

0 commit comments

Comments
 (0)