Skip to content

Commit

Permalink
Fix scala#174: Register names for types and symbols
Browse files Browse the repository at this point in the history
The following commit fixes the issues with as seen from that are
explained in this fantastic issue by Greg: scala#174, as well as scala#239 that
handles structural types.

These issues are related to the fact that our previous approach was only
inspecting types, when some type information like type bounds is only
present in symbols. To get that information, we need a more precise
search that looks into the core of the Scalac types for the required
information.

Of course, type bounds is not all we're interested about. The issue is
that type members, method parameters and type parameters can have type
information in its definition that is necessary at the use site to
detect and propagate changes. This information is also tied to the fact
that type members can have different materializations depending on the
prefix (both because of type members and path-dependent types).

`types-in-used-names-b` and `as-seen-from-b` are a perfect example of
this. This commit turns them into passing tests.

Having more in-depth look at the algorithm behind it will help us
understand what it does. In essence, the new type traverser is the
responsible of adding dependencies on every `TypeRef` and `SingleType`.
They contain concrete information about types (they are materialized),
so their presence must be recorded.

We also have the presence of other types like `PolyType` and
`MethodType`. These types are used for defining type parameters for
classes (think List[A]) and method type parameters (think def foo[T](t:
T)). They are nested, meaning that their return type can also be a
`PolyType` or a `MethodType`. To handle them, we traverse the symbols in
their definition -- for method types we traverse the types of the
parameters, while for poly types we add directly the dependency on the
symbol --so that the name of the type parameters are also recorded-- and
then we continue checking for their information if they are not a class,
that is, if they are an abstract type with a definition that we may need
to traverse (existential type, refined type, bounds, etc).

In the case of `TypeBounds`, we traverse them if they are not the
default specified by the SLS (`Nothing` for low bound, `Any` for high).

Refined types need special handling since we need to check their
declarations, that can introduce new type members or vals. If they do
have them, we add a dependency right away on those definitions.

As usual, `ThisType` and `ConstantType` need to be inspected by checking
their underlying representation (`C` in `C.this` and `12` in `Int(12)`).

`ExistentialType`, the last type on the traverser before falling back to
`mapOver`, has a list of symbols called `quantified` that needs to be
traversed since they are the symbol information that constrain the
existential type. As in the case of `TypeBounds`, we guard against the
default types `Nothing` for low bound and `Any` for high bound, so that
unnecessary names that are always present in source files don't appear.

This change triggers a very weird behaviour in 2.10, in which for some
reason the names `Nothing` and `Any` appear. This does not seem to come
from the new TypeDependencyTraverser and I've been able to track its
appearance to the case in the traverser where we check for `hasSymbol`
and add with `addSymbol`. I've added a TODO, which is not urgent, to
find out what's happening, since this only affect one concrete snippet
of the whole test code.

Benchmark:

```
[info] # Run complete. Total time: 00:25:51
[info]
[info] Benchmark                                                            (_tempDir)    Mode  Cnt           Score            Error   Units
[info] HotScalacBenchmark.compile                                    /tmp/sbt_abdb5ed2  sample   18       20893.226 ±        625.622   ms/op
[info] HotScalacBenchmark.compile:compile·p0.00                      /tmp/sbt_abdb5ed2  sample            19797.115                    ms/op
[info] HotScalacBenchmark.compile:compile·p0.50                      /tmp/sbt_abdb5ed2  sample            21005.074                    ms/op
[info] HotScalacBenchmark.compile:compile·p0.90                      /tmp/sbt_abdb5ed2  sample            21894.267                    ms/op
[info] HotScalacBenchmark.compile:compile·p0.95                      /tmp/sbt_abdb5ed2  sample            22045.262                    ms/op
[info] HotScalacBenchmark.compile:compile·p0.99                      /tmp/sbt_abdb5ed2  sample            22045.262                    ms/op
[info] HotScalacBenchmark.compile:compile·p0.999                     /tmp/sbt_abdb5ed2  sample            22045.262                    ms/op
[info] HotScalacBenchmark.compile:compile·p0.9999                    /tmp/sbt_abdb5ed2  sample            22045.262                    ms/op
[info] HotScalacBenchmark.compile:compile·p1.00                      /tmp/sbt_abdb5ed2  sample            22045.262                    ms/op
[info] HotScalacBenchmark.compile:·gc.alloc.rate                     /tmp/sbt_abdb5ed2  sample   18         289.838 ±          8.669  MB/sec
[info] HotScalacBenchmark.compile:·gc.alloc.rate.norm                /tmp/sbt_abdb5ed2  sample   18  6500730176.000 ±   13633760.029    B/op
[info] HotScalacBenchmark.compile:·gc.churn.PS_Eden_Space            /tmp/sbt_abdb5ed2  sample   18         289.082 ±         24.260  MB/sec
[info] HotScalacBenchmark.compile:·gc.churn.PS_Eden_Space.norm       /tmp/sbt_abdb5ed2  sample   18  6480403569.778 ±  464987965.594    B/op
[info] HotScalacBenchmark.compile:·gc.churn.PS_Old_Gen               /tmp/sbt_abdb5ed2  sample   18          12.679 ±         12.697  MB/sec
[info] HotScalacBenchmark.compile:·gc.churn.PS_Old_Gen.norm          /tmp/sbt_abdb5ed2  sample   18   290767194.667 ±  290528363.065    B/op
[info] HotScalacBenchmark.compile:·gc.churn.PS_Survivor_Space        /tmp/sbt_abdb5ed2  sample   18           7.321 ±          2.865  MB/sec
[info] HotScalacBenchmark.compile:·gc.churn.PS_Survivor_Space.norm   /tmp/sbt_abdb5ed2  sample   18   165547052.444 ±   66661097.019    B/op
[info] HotScalacBenchmark.compile:·gc.count                          /tmp/sbt_abdb5ed2  sample   18         101.000                   counts
[info] HotScalacBenchmark.compile:·gc.time                           /tmp/sbt_abdb5ed2  sample   18       21332.000                       ms
[info] WarmScalacBenchmark.compile                                   /tmp/sbt_abdb5ed2  sample    3       52769.937 ±       6743.004   ms/op
[info] WarmScalacBenchmark.compile:compile·p0.00                     /tmp/sbt_abdb5ed2  sample            52412.023                    ms/op
[info] WarmScalacBenchmark.compile:compile·p0.50                     /tmp/sbt_abdb5ed2  sample            52747.567                    ms/op
[info] WarmScalacBenchmark.compile:compile·p0.90                     /tmp/sbt_abdb5ed2  sample            53150.220                    ms/op
[info] WarmScalacBenchmark.compile:compile·p0.95                     /tmp/sbt_abdb5ed2  sample            53150.220                    ms/op
[info] WarmScalacBenchmark.compile:compile·p0.99                     /tmp/sbt_abdb5ed2  sample            53150.220                    ms/op
[info] WarmScalacBenchmark.compile:compile·p0.999                    /tmp/sbt_abdb5ed2  sample            53150.220                    ms/op
[info] WarmScalacBenchmark.compile:compile·p0.9999                   /tmp/sbt_abdb5ed2  sample            53150.220                    ms/op
[info] WarmScalacBenchmark.compile:compile·p1.00                     /tmp/sbt_abdb5ed2  sample            53150.220                    ms/op
[info] WarmScalacBenchmark.compile:·gc.alloc.rate                    /tmp/sbt_abdb5ed2  sample    3         125.382 ±         13.840  MB/sec
[info] WarmScalacBenchmark.compile:·gc.alloc.rate.norm               /tmp/sbt_abdb5ed2  sample    3  7055970890.667 ± 1078954896.900    B/op
[info] WarmScalacBenchmark.compile:·gc.churn.PS_Eden_Space           /tmp/sbt_abdb5ed2  sample    3         117.215 ±         73.864  MB/sec
[info] WarmScalacBenchmark.compile:·gc.churn.PS_Eden_Space.norm      /tmp/sbt_abdb5ed2  sample    3  6596470733.333 ± 4281843293.325    B/op
[info] WarmScalacBenchmark.compile:·gc.churn.PS_Survivor_Space       /tmp/sbt_abdb5ed2  sample    3           2.279 ±          1.015  MB/sec
[info] WarmScalacBenchmark.compile:·gc.churn.PS_Survivor_Space.norm  /tmp/sbt_abdb5ed2  sample    3   128269752.000 ±   72721263.065    B/op
[info] WarmScalacBenchmark.compile:·gc.count                         /tmp/sbt_abdb5ed2  sample    3          73.000                   counts
[info] WarmScalacBenchmark.compile:·gc.time                          /tmp/sbt_abdb5ed2  sample    3        8746.000                       ms
[info] ColdScalacBenchmark.compile                                   /tmp/sbt_abdb5ed2      ss   10       44611.286 ±        963.131   ms/op
[info] ColdScalacBenchmark.compile:·gc.alloc.rate                    /tmp/sbt_abdb5ed2      ss   10         152.054 ±          2.753  MB/sec
[info] ColdScalacBenchmark.compile:·gc.alloc.rate.norm               /tmp/sbt_abdb5ed2      ss   10  7249761568.800 ±   95126804.264    B/op
[info] ColdScalacBenchmark.compile:·gc.churn.PS_Eden_Space           /tmp/sbt_abdb5ed2      ss   10         144.481 ±          9.964  MB/sec
[info] ColdScalacBenchmark.compile:·gc.churn.PS_Eden_Space.norm      /tmp/sbt_abdb5ed2      ss   10  6889406191.200 ±  490961958.245    B/op
[info] ColdScalacBenchmark.compile:·gc.churn.PS_Old_Gen              /tmp/sbt_abdb5ed2      ss   10          ≈ 10⁻³                   MB/sec
[info] ColdScalacBenchmark.compile:·gc.churn.PS_Old_Gen.norm         /tmp/sbt_abdb5ed2      ss   10       21136.000 ±     101049.368    B/op
[info] ColdScalacBenchmark.compile:·gc.churn.PS_Survivor_Space       /tmp/sbt_abdb5ed2      ss   10           2.848 ±          0.335  MB/sec
[info] ColdScalacBenchmark.compile:·gc.churn.PS_Survivor_Space.norm  /tmp/sbt_abdb5ed2      ss   10   135792956.800 ±   16291050.509    B/op
[info] ColdScalacBenchmark.compile:·gc.count                         /tmp/sbt_abdb5ed2      ss   10         248.000                   counts
[info] ColdScalacBenchmark.compile:·gc.time                          /tmp/sbt_abdb5ed2      ss   10       29901.000                       ms
[success] Total time: 1553 s, completed Feb 26, 2017 3:06:29 AM
[success] Total time: 0 s, completed Feb 26, 2017 3:06:29 AM
```

Rewritten from sbt/zinc@929b758
  • Loading branch information
jvican committed Feb 26, 2017
1 parent 57d2439 commit acb8236
Show file tree
Hide file tree
Showing 10 changed files with 311 additions and 45 deletions.
32 changes: 18 additions & 14 deletions src-2.10/main/scala/xsbt/Dependency.scala
Original file line number Diff line number Diff line change
Expand Up @@ -228,11 +228,19 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
private def addTreeDependency(tree: Tree): Unit = {
addDependency(tree.symbol)
val tpe = tree.tpe
if (!ignoredType(tpe))
foreachNotPackageSymbolInType(tpe)(addDependency)
if (!ignoredType(tpe)) {
addTypeDependencies(tpe)
}
()
}

def addTypeDependencies(tpe: Type): Unit = {
// Defined in GlobalHelpers.scala
object TypeDependencyTraverser extends TypeDependencyTraverser(addDependency)
TypeDependencyTraverser.traverse(tpe)
TypeDependencyTraverser.reinitializeVisited()
}

private def addDependency(dep: Symbol): Unit = {
val fromClass = resolveDependencySource().fromClass
if (ignoredSymbol(fromClass) || fromClass.hasPackageFlag) {
Expand Down Expand Up @@ -305,15 +313,19 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with

debuglog("Parent types for " + tree.symbol + " (self: " + self.tpt.tpe + "): " + inheritanceTypes + " with symbols " + inheritanceSymbols.map(_.fullName))

inheritanceSymbols.foreach(addSymbolFromParent)
inheritanceTypes.foreach(addSymbolsFromType)
addSymbolsFromType(self.tpt.tpe)
inheritanceSymbols.foreach { symbol =>
addInheritanceDependency(symbol)
addDependency(symbol)
}

inheritanceTypes.foreach(addTypeDependencies)
addTypeDependencies(self.tpt.tpe)

traverseTrees(body)

// In some cases (eg. macro annotations), `typeTree.tpe` may be null. See sbt/sbt#1593 and sbt/sbt#1655.
case typeTree: TypeTree if !ignoredType(typeTree.tpe) =>
foreachNotPackageSymbolInType(typeTree.tpe)(addDependency)
addTypeDependencies(typeTree.tpe)
case m @ MacroExpansionOf(original) if inspectedOriginalTrees.add(original) =>
traverse(original)
super.traverse(m)
Expand All @@ -325,13 +337,5 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
super.traverse(tree)
case other => super.traverse(other)
}

val addSymbolFromParent: Symbol => Unit = { symbol =>
addInheritanceDependency(symbol)
addDependency(symbol)
}
val addSymbolsFromType: Type => Unit = { tpe =>
foreachNotPackageSymbolInType(tpe)(addDependency)
}
}
}
14 changes: 11 additions & 3 deletions src-2.10/main/scala/xsbt/ExtractUsedNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) ext
// Synthetic names are no longer included. See https://github.com/sbt/sbt/issues/2537
if (!isEmptyName(name) && !enclosingNonLocalClass.containsName(name))
enclosingNonLocalClass.addName(name)
()
}
}

Expand Down Expand Up @@ -123,6 +124,8 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) ext
}
}

object TypeDependencyTraverser extends TypeDependencyTraverser(addSymbol)

private def handleClassicTreeNode(tree: Tree): Unit = tree match {
case _: DefTree | _: Template => ()
case Import(_, selectors: List[ImportSelector]) =>
Expand All @@ -149,9 +152,14 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) ext
original.foreach(traverse)
}
case t if t.hasSymbol =>
addSymbol(t.symbol)
if (t.tpe != null)
foreachNotPackageSymbolInType(t.tpe)(addSymbol)
val symbol = t.symbol
if (symbol != rootMirror.RootPackage)
addSymbol(t.symbol)
val tpe = t.tpe
if (!ignoredType(tpe)) {
TypeDependencyTraverser.traverse(tpe)
TypeDependencyTraverser.reinitializeVisited()
}
case _ =>
}

Expand Down
88 changes: 88 additions & 0 deletions src-2.10/main/scala/xsbt/GlobalHelpers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,94 @@ trait GlobalHelpers {
}).traverse(tpe)
}

private[xsbt] class TypeDependencyTraverser(addDependency: Symbol => Unit)
extends TypeTraverser {

/** Add type dependency ignoring packages and inheritance info from classes. */
@inline private def addTypeSymbolDependency(symbol: Symbol): Unit = {
addDependency(symbol)
if (!symbol.isClass) {
traverse(symbol.info)
}
}

/** Add type dependency *AND* traverse prefix iff is not a package. */
@inline private def addTypeDependency(tpe: Type): Unit = {
val symbol = tpe.typeSymbolDirect
if (!symbol.hasPackageFlag) {
addTypeSymbolDependency(symbol)
traverse(tpe.prefix)
}
}

// Define cache and populate it with known types at initialization time
private val visited = scala.collection.mutable.HashSet.empty[Type]

/** Clear the cache after every `traverse` invocation at the call-site. */
private[xsbt] def reinitializeVisited(): Unit = visited.clear()

/**
* Traverse the type and its info to track all type dependencies.
*
* Note that tpe cannot be either `NoSymbol` or `null`.
* Check that you don't pass those types at the call-site.
*/
override def traverse(tpe: Type): Unit = {
if ((tpe ne NoType) && !visited.contains(tpe)) {
visited += tpe
tpe match {
case singleRef: SingleType =>
addTypeDependency(singleRef)

case typeRef: TypeRef =>
// Traverse materialized type arguments
typeRef.typeArguments.foreach(traverse)
addTypeDependency(typeRef)

case MethodType(_, _) =>
// Traverse the types of method parameters definitions
tpe.params.foreach(param => traverse(param.tpe))
// Traverse return type
traverse(tpe.resultType)

case PolyType(_, _) =>
// Traverse the symbols of poly types and their prefixes
tpe.typeParams.foreach { typeParam =>
addTypeSymbolDependency(typeParam)
val prefix = typeParam.info.prefix
if (!prefix.typeSymbolDirect.hasPackageFlag)
traverse(prefix)
}
// Traverse return type
traverse(tpe.resultType)

case TypeBounds(lo, hi) =>
// Ignore default types for lo and hi bounds
if (!(lo == definitions.NothingTpe)) traverse(lo)
if (!(hi == definitions.AnyTpe)) traverse(hi)

case RefinedType(parents, decls) =>
parents.foreach(traverse)
decls.toIterator.foreach { decl =>
if (decl.isType) addTypeSymbolDependency(decl)
else addDependency(decl)
}

case ExistentialType(quantified, underlying) =>
quantified.foreach(quantified => traverse(quantified.tpe))
traverse(underlying)

case ThisType(_) | ConstantType(_) =>
traverse(tpe.underlying)

case _ =>
mapOver(tpe)
()
}
}
}
}

/** Returns true if given tree contains macro attchment. In such case calls func on tree from attachment. */
def processMacroExpansion(in: Tree)(func: Tree => Unit): Boolean = {
// Hotspot
Expand Down
32 changes: 18 additions & 14 deletions src/main/scala/xsbt/Dependency.scala
Original file line number Diff line number Diff line change
Expand Up @@ -249,11 +249,19 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
private def addTreeDependency(tree: Tree): Unit = {
addDependency(tree.symbol)
val tpe = tree.tpe
if (!ignoredType(tpe))
foreachNotPackageSymbolInType(tpe)(addDependency)
if (!ignoredType(tpe)) {
addTypeDependencies(tpe)
}
()
}

def addTypeDependencies(tpe: Type): Unit = {
// Defined in GlobalHelpers.scala
object TypeDependencyTraverser extends TypeDependencyTraverser(addDependency)
TypeDependencyTraverser.traverse(tpe)
TypeDependencyTraverser.reinitializeVisited()
}

private def addDependency(dep: Symbol): Unit = {
val fromClass = resolveDependencySource
if (ignoredSymbol(fromClass) || fromClass.hasPackageFlag) {
Expand Down Expand Up @@ -325,15 +333,19 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with

debuglog("Parent types for " + tree.symbol + " (self: " + self.tpt.tpe + "): " + inheritanceTypes + " with symbols " + inheritanceSymbols.map(_.fullName))

inheritanceSymbols.foreach(addSymbolFromParent)
inheritanceTypes.foreach(addSymbolsFromType)
addSymbolsFromType(self.tpt.tpe)
inheritanceSymbols.foreach { symbol =>
addInheritanceDependency(symbol)
addDependency(symbol)
}

inheritanceTypes.foreach(addTypeDependencies)
addTypeDependencies(self.tpt.tpe)

traverseTrees(body)

// In some cases (eg. macro annotations), `typeTree.tpe` may be null. See sbt/sbt#1593 and sbt/sbt#1655.
case typeTree: TypeTree if !ignoredType(typeTree.tpe) =>
foreachNotPackageSymbolInType(typeTree.tpe)(addDependency)
addTypeDependencies(typeTree.tpe)
case m @ MacroExpansionOf(original) if inspectedOriginalTrees.add(original) =>
traverse(original)
super.traverse(m)
Expand All @@ -345,13 +357,5 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
super.traverse(tree)
case other => super.traverse(other)
}

val addSymbolFromParent: Symbol => Unit = { symbol =>
addInheritanceDependency(symbol)
addDependency(symbol)
}
val addSymbolsFromType: Type => Unit = { tpe =>
foreachNotPackageSymbolInType(tpe)(addDependency)
}
}
}
10 changes: 8 additions & 2 deletions src/main/scala/xsbt/ExtractUsedNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) ext
// Synthetic names are no longer included. See https://github.com/sbt/sbt/issues/2537
if (!isEmptyName(name) && !names.contains(name))
names += name
()
}
}

Expand Down Expand Up @@ -130,6 +131,8 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) ext
}
}

object TypeDependencyTraverser extends TypeDependencyTraverser(addSymbol)

private def handleClassicTreeNode(tree: Tree): Unit = tree match {
case _: DefTree | _: Template => ()
case Import(_, selectors: List[ImportSelector]) =>
Expand All @@ -156,8 +159,11 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) ext
}
case t if t.hasSymbolField =>
addSymbol(t.symbol)
if (t.tpe != null)
foreachNotPackageSymbolInType(t.tpe)(addSymbol)
val tpe = t.tpe
if (!ignoredType(tpe)) {
TypeDependencyTraverser.traverse(tpe)
TypeDependencyTraverser.reinitializeVisited()
}
case _ =>
}

Expand Down
88 changes: 88 additions & 0 deletions src/main/scala/xsbt/GlobalHelpers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,94 @@ trait GlobalHelpers {
}).traverse(tpe)
}

private[xsbt] class TypeDependencyTraverser(addDependency: Symbol => Unit)
extends TypeTraverser {

/** Add type dependency ignoring packages and inheritance info from classes. */
@inline private def addTypeSymbolDependency(symbol: Symbol): Unit = {
addDependency(symbol)
if (!symbol.isClass) {
traverse(symbol.info)
}
}

/** Add type dependency *AND* traverse prefix iff is not a package. */
@inline private def addTypeDependency(tpe: Type): Unit = {
val symbol = tpe.typeSymbolDirect
if (!symbol.hasPackageFlag) {
addTypeSymbolDependency(symbol)
traverse(tpe.prefix)
}
}

// Define cache and populate it with known types at initialization time
private val visited = scala.collection.mutable.HashSet.empty[Type]

/** Clear the cache after every `traverse` invocation at the call-site. */
private[xsbt] def reinitializeVisited(): Unit = visited.clear()

/**
* Traverse the type and its info to track all type dependencies.
*
* Note that tpe cannot be either `NoSymbol` or `null`.
* Check that you don't pass those types at the call-site.
*/
override def traverse(tpe: Type): Unit = {
if ((tpe ne NoType) && !visited.contains(tpe)) {
visited += tpe
tpe match {
case singleRef: SingleType =>
addTypeDependency(singleRef)

case typeRef: TypeRef =>
// Traverse materialized type arguments
typeRef.typeArguments.foreach(traverse)
addTypeDependency(typeRef)

case MethodType(_, _) =>
// Traverse the types of method parameters definitions
tpe.params.foreach(param => traverse(param.tpe))
// Traverse return type
traverse(tpe.resultType)

case PolyType(_, _) =>
// Traverse the symbols of poly types and their prefixes
tpe.typeParams.foreach { typeParam =>
addTypeSymbolDependency(typeParam)
val prefix = typeParam.info.prefix
if (!prefix.typeSymbolDirect.hasPackageFlag)
traverse(prefix)
}
// Traverse return type
traverse(tpe.resultType)

case TypeBounds(lo, hi) =>
// Ignore default types for lo and hi bounds
if (!(lo == definitions.NothingTpe)) traverse(lo)
if (!(hi == definitions.AnyTpe)) traverse(hi)

case RefinedType(parents, decls) =>
parents.foreach(traverse)
decls.toIterator.foreach { decl =>
if (decl.isType) addTypeSymbolDependency(decl)
else addDependency(decl)
}

case ExistentialType(quantified, underlying) =>
quantified.foreach(quantified => traverse(quantified.tpe))
traverse(underlying)

case ThisType(_) | ConstantType(_) =>
traverse(tpe.underlying)

case _ =>
mapOver(tpe)
()
}
}
}
}

/** Returns true if given tree contains macro attchment. In such case calls func on tree from attachment. */
def processMacroExpansion(in: Tree)(func: Tree => Unit): Boolean = {
// Hotspot
Expand Down
4 changes: 2 additions & 2 deletions src/test/scala/xsbt/DependencySpecification.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class DependencySpecification extends UnitSpec {
assert(inheritance("D") === Set.empty)
assert(memberRef("E") === Set.empty)
assert(inheritance("E") === Set.empty)
assert(memberRef("F") === Set("A", "B", "D", "E", "G"))
assert(memberRef("F") === Set("A", "B", "D", "E", "G", "C")) // C is the underlying type of MyC
assert(inheritance("F") === Set("A", "E"))
assert(memberRef("H") === Set("B", "E", "G"))
// aliases and applied type constructors are expanded so we have inheritance dependency on B
Expand Down Expand Up @@ -88,7 +88,7 @@ class DependencySpecification extends UnitSpec {
val inheritance = classDependencies.inheritance
assert(memberRef("Outer") === Set.empty)
assert(inheritance("Outer") === Set.empty)
assert(memberRef("Bar") === Set("Outer"))
assert(memberRef("Bar") === Set("Outer", "Outer.Inner"))
assert(inheritance("Bar") === Set.empty)
}

Expand Down
Loading

0 comments on commit acb8236

Please sign in to comment.