Skip to content

Commit

Permalink
Fix naming of inherited classes in ExtractAPI
Browse files Browse the repository at this point in the history
For an inherited class, ExtractAPI would form a (source) class name by
calling `Symbol.className` on the inherited class. However, that created
a name of a class as seen at declaration site and not at inheritance site.
Let's consider an example:

class A { class AA }
class B extends A

Before this change, ExtractAPI would create an API representation of `AA`
twice: once seen from A, and then the second time seen from B as an
inherited member. However, in both cases it would use `A.AA` as a name.
This commit fixes naming so an inherited representation of `AA` has
a name `B.AA`.

This commit also clarifies how classes declared in package objects
are named. If you have:

package pkg1.pkg2
package object pkg3 {
  class Foo
}

then the fully qualified name of the class corresponding to `pkg3` package
object is pkg1.pkg2.pkg3.package. The full name of the `Foo` class is
pkg2.pkg2.pkg3.Foo.
  • Loading branch information
gkossakowski committed May 3, 2016
1 parent 1a696ed commit 459df6b
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 3 deletions.
15 changes: 15 additions & 0 deletions internal/compiler-bridge/src-2.10/main/scala/xsbt/ClassName.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,21 @@ trait ClassName {
*/
protected def className(s: Symbol): String = pickledName(s)

/**
* Create a (source) name for the class symbol `s` with a prefix determined by the class symbol `in`.
*
* If `s` represents a package object `pkg3`, then the returned name will be `pkg1.pkg2.pkg3.package`.
* If `s` represents a class `Foo` nested in package object `pkg3` then the returned name is `pkg1.pkg2.pk3.Foo`.
*/
protected def classNameAsSeenIn(in: Symbol, s: Symbol): String = atPhase(currentRun.picklerPhase.next) {
if (in.isRoot || in.isRootPackage || in == NoSymbol || in.isEffectiveRoot)
s.simpleName.toString
else if (in.isPackageObjectOrClass)
in.owner.fullName + "." + s.name
else
in.fullName + "." + s.name
}

private def pickledName(s: Symbol): String =
atPhase(currentRun.picklerPhase) { s.fullName }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ class ExtractAPI[GlobalType <: Global](
val anns = annotations(in, c)
val modifiers = getModifiers(c)
val acc = getAccess(c)
val name = className(c)
val name = classNameAsSeenIn(in, c)
val tParams = typeParameters(in, sym) // look at class symbol
val selfType = lzy(this.selfType(in, sym))
def constructClass(structure: xsbti.api.Lazy[Structure]): ClassLike = {
Expand Down
15 changes: 15 additions & 0 deletions internal/compiler-bridge/src/main/scala/xsbt/ClassName.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,21 @@ trait ClassName {
*/
protected def className(s: Symbol): String = pickledName(s)

/**
* Create a (source) name for the class symbol `s` with a prefix determined by the class symbol `in`.
*
* If `s` represents a package object `pkg3`, then the returned name will be `pkg1.pkg2.pkg3.package`.
* If `s` represents a class `Foo` nested in package object `pkg3` then the returned name is `pkg1.pkg2.pk3.Foo`.
*/
protected def classNameAsSeenIn(in: Symbol, s: Symbol): String = enteringPhase(currentRun.picklerPhase.next) {
if (in.isRoot || in.isRootPackage || in == NoSymbol || in.isEffectiveRoot)
s.simpleName.toString
else if (in.isPackageObjectOrClass)
in.owner.fullName + "." + s.name
else
in.fullName + "." + s.name
}

private def pickledName(s: Symbol): String =
enteringPhase(currentRun.picklerPhase.next) { s.fullName }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ class ExtractAPI[GlobalType <: Global](
val anns = annotations(in, c)
val modifiers = getModifiers(c)
val acc = getAccess(c)
val name = className(c)
val name = classNameAsSeenIn(in, c)
val tParams = typeParameters(in, sym) // look at class symbol
val selfType = lzy(this.selfType(in, sym))
def constructClass(structure: xsbti.api.Lazy[Structure]): ClassLike = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ class ExtractAPISpecification extends UnitSpec {
* is compiled together with Namers or Namers is compiled first and then Global refers
* to Namers by unpickling types from class files.
*/
it should "make a stable representation of a self variable that has no self type" in pendingUntilFixed {
it should "make a stable representation of a self variable that has no self type" in {
def selectNamer(apis: Set[ClassLike]): ClassLike = {
// TODO: this doesn't work yet because inherited classes are not extracted
apis.find(_.name == "Global.Foo.Namer").get
Expand All @@ -145,6 +145,33 @@ class ExtractAPISpecification extends UnitSpec {
assert(SameAPI(namerApi1, namerApi2))
}

it should "make a different representation for an inherited class" in {
val src =
"""|class A[T] {
| abstract class AA { def t: T }
|}
|class B extends A[Int]
""".stripMargin
val compilerForTesting = new ScalaCompilerForUnitTesting
val apis = compilerForTesting.extractApisFromSrc(src).map(a => a.name -> a).toMap
assert(apis.keySet === Set("A", "A.AA", "B", "B.AA"))
assert(apis("A.AA") !== apis("B.AA"))
}

it should "handle package objects and type companions" in {
val src =
"""|package object abc {
| type BuildInfoKey = BuildInfoKey.Entry[_]
| object BuildInfoKey {
| sealed trait Entry[A]
| }
|}
""".stripMargin
val compilerForTesting = new ScalaCompilerForUnitTesting
val apis = compilerForTesting.extractApisFromSrc(src).map(a => a.name -> a).toMap
assert(apis.keySet === Set("abc.package", "abc.BuildInfoKey", "abc.BuildInfoKey.Entry"))
}

/**
* Checks if self type is properly extracted in various cases of declaring a self type
* with our without a self variable.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package object abc {
object BuildInfoKey {
sealed trait Entry
}
class Foo
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
> compile

0 comments on commit 459df6b

Please sign in to comment.