Skip to content

Commit d09144d

Browse files
committed
Handle experimental nested classes (and their methods)
1 parent b19ae44 commit d09144d

File tree

8 files changed

+65
-23
lines changed

8 files changed

+65
-23
lines changed

core/src/main/scala/com/typesafe/tools/mima/core/TastyUnpickler.scala

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import TastyFormat._, NameTags._, TastyTagOps._, TastyRefs._
99

1010
object TastyUnpickler {
1111
def unpickleClass(in: TastyReader, clazz: ClassInfo, path: String): Unit = {
12-
val doPrint = false
12+
//val doPrint = false
1313
//val doPrint = path.contains("v1") && !path.contains("experimental2.tasty")
1414
//if (doPrint) TastyPrinter.printClassNames(in.fork, path)
1515
//if (doPrint) TastyPrinter.printPickle(in.fork, path)
@@ -23,7 +23,13 @@ object TastyUnpickler {
2323

2424
object trav extends Traverser {
2525
var pkgNames = List.empty[Name]
26-
val classes = new mutable.ListBuffer[(Name, ClsDef)]
26+
var clsNames = List.empty[Name]
27+
28+
def dropHead[A](xs: List[A], head: A) = xs match {
29+
case `head` :: ys => ys
30+
case x :: _ => throw new AssertionError(s"assertion failed: Expected head=$head but it was $x")
31+
case _ => throw new AssertionError(s"assertion failed: Expected head=$head but list was empty")
32+
}
2733

2834
override def traversePkg(pkg: Pkg): Unit = {
2935
val pkgName = pkg.path match {
@@ -32,21 +38,22 @@ object TastyUnpickler {
3238
}
3339
pkgNames ::= pkgName
3440
super.traversePkg(pkg)
35-
pkgNames match {
36-
case n :: ns => pkgNames = ns.ensuring(n == pkgName, s"last=$n pkgName=$pkgName")
37-
case _ => assert(false, s"Expected $pkgName as last pkg name, was empty")
38-
}
41+
pkgNames = dropHead(pkgNames, pkgName)
3942
}
4043

4144
override def traverseClsDef(clsDef: ClsDef): Unit = {
42-
classes += ((pkgNames.headOption.getOrElse(nme.Empty), clsDef))
45+
forEachClass(clsDef, pkgNames, clsNames)
46+
clsNames ::= clsDef.name
4347
super.traverseClsDef(clsDef)
48+
clsNames = dropHead(clsNames, clsDef.name)
4449
}
4550
}
46-
trav.traverse(tree)
47-
trav.classes.toList.foreach { case (pkgName, clsDef) =>
51+
52+
def forEachClass(clsDef: ClsDef, pkgNames: List[Name], clsNames: List[Name]): Unit = {
53+
val pkgName = pkgNames.headOption.getOrElse(nme.Empty)
4854
if (pkgName.source == clazz.owner.fullName) {
49-
val cls = clazz.owner.classes.getOrElse(clsDef.name.source, NoClass)
55+
val clsName = (clsDef.name :: clsNames).reverseIterator.mkString("$")
56+
val cls = clazz.owner.classes.getOrElse(clsName, NoClass)
5057
if (cls != NoClass) {
5158
cls._experimental |= clsDef.annots.exists(_.tycon.toString == "scala.annotation.experimental")
5259
cls._experimental |= clsDef.annots.exists(_.tycon.toString == "scala.annotation.experimental2")
@@ -62,6 +69,8 @@ object TastyUnpickler {
6269
}
6370
}
6471
}
72+
73+
trav.traverse(tree)
6574
}
6675

6776
def unpickleTree(in: TastyReader, names: Names): Tree = {
@@ -108,7 +117,7 @@ object TastyUnpickler {
108117
DefDef(name, annots)
109118
}
110119

111-
def readTemplate() = {
120+
def readTemplate(): Template = {
112121
// TypeParam* TermParam* parent_Term* Self? Stat* -- [typeparams] paramss extends parents { self => stats }, where Stat* always starts with the primary constructor.
113122
// TypeParam = TYPEPARAM Length NameRef type_Term Modifier* -- modifiers name bounds
114123
// TermParam = PARAM Length NameRef type_Term Modifier* -- modifiers name : type.
@@ -121,12 +130,14 @@ object TastyUnpickler {
121130
while (nextByte == PARAM || nextByte == EMPTYCLAUSE || nextByte == SPLITCLAUSE) skipTree(readByte()) // tparams
122131
while (nextByte != SELFDEF && nextByte != DEFDEF) skipTree(readByte()) // parents
123132
if (nextByte == SELFDEF) skipTree(readByte()) // self
133+
val classes = new ListBuffer[ClsDef]
124134
val meths = new ListBuffer[DefDef]
125135
doUntil(end)(readByte match {
126-
case DEFDEF => meths += readDefDef()
127-
case tag => skipTree(tag)
136+
case TYPEDEF => readTypeDef() match { case clsDef: ClsDef => classes += clsDef case _ => }
137+
case DEFDEF => meths += readDefDef()
138+
case tag => skipTree(tag)
128139
})
129-
Template(meths.toList)
140+
Template(classes.toList, meths.toList)
130141
}
131142

132143
def readClassDef(name: Name, end: Addr) = ClsDef(name.toTypeName, readTemplate(), readAnnotsInMods(end)) // NameRef Template Modifier* -- modifiers class name template
@@ -199,7 +210,7 @@ object TastyUnpickler {
199210
final case class Pkg(path: Path, trees: List[Tree]) extends Tree { def show = s"package $path${trees.map("\n " + _).mkString}" }
200211

201212
final case class ClsDef(name: TypeName, template: Template, annots: List[Annot]) extends Tree { def show = s"${annots.map("" + _ + " ").mkString}class $name$template" }
202-
final case class Template(meths: List[DefDef]) extends Tree { def show = s"${meths.map("\n " + _).mkString}" }
213+
final case class Template(classes: List[ClsDef], meths: List[DefDef]) extends Tree { def show = s"${(classes ::: meths).map("\n " + _).mkString}" }
203214
final case class DefDef(name: Name, annots: List[Annot] = Nil) extends Tree { def show = s"${annots.map("" + _ + " ").mkString}def $name" }
204215

205216
sealed trait Type extends Tree
@@ -225,14 +236,11 @@ object TastyUnpickler {
225236

226237
def traverseName(name: Name) = ()
227238

228-
def traverseTrees(trees: List[Tree]) = trees.foreach(traverse)
229-
def traverseAnnots(annots: List[Annot]) = annots.foreach(traverse)
230-
231-
def traversePkg(pkg: Pkg) = { traverse(pkg.path); traverseTrees(pkg.trees) }
232-
def traverseClsDef(clsDef: ClsDef) = { traverseName(clsDef.name); traverseAnnots(clsDef.annots) }
233-
def traverseTemplate(tmpl: Template) = { traverseTrees(tmpl.meths) }
234-
def traverseDefDef(defDef: DefDef) = { traverseName(defDef.name); traverseAnnots(defDef.annots) }
235-
def traverseAnnot(annot: Annot) = { traverse(annot.tycon); traverse(annot.fullAnnotation) }
239+
def traversePkg(pkg: Pkg) = { val Pkg(path, trees) = pkg; traverse(path); trees.foreach(traverse) }
240+
def traverseClsDef(clsDef: ClsDef) = { val ClsDef(name, tmpl, annots) = clsDef; traverseName(name); traverseTemplate(tmpl); annots.foreach(traverse) }
241+
def traverseTemplate(tmpl: Template) = { val Template(classes, meths) = tmpl; classes.foreach(traverse); meths.foreach(traverse) }
242+
def traverseDefDef(defDef: DefDef) = { val DefDef(name, annots) = defDef; traverseName(name); annots.foreach(traverse) }
243+
def traverseAnnot(annot: Annot) = { val Annot(tycon, fullAnnotation) = annot; traverse(tycon); traverse(fullAnnotation) }
236244

237245
def traversePath(path: Path) = path match {
238246
case TypeRefPkg(fullyQual) => traverseName(fullyQual)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object App {
2+
def main(args: Array[String]): Unit = ()
3+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Haven't implemented the ScalaSig unpickling part, only TASTy
2+
abstract method errorAndAbort(java.lang.String)scala.runtime.Nothing# in interface pkg1.pkg2.Quotes#reflectModule#reportModule is present only in new version

functional-tests/src/test/changes-in-experimental-nested-are-ok/problems.txt

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Haven't implemented the ScalaSig unpickling part, only TASTy
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package pkg1
2+
package pkg2
3+
4+
trait Quotes {
5+
val reflect: reflectModule
6+
trait reflectModule {
7+
val report: reportModule
8+
trait reportModule
9+
}
10+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package pkg1
2+
package pkg2
3+
4+
import scala.annotation.experimental2
5+
6+
trait Quotes {
7+
val reflect: reflectModule
8+
trait reflectModule {
9+
val report: reportModule
10+
trait reportModule {
11+
@experimental2
12+
def errorAndAbort(msg: String): Nothing
13+
}
14+
}
15+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package scala.annotation
2+
3+
class experimental2 extends StaticAnnotation

0 commit comments

Comments
 (0)