From c47aa70620b1d294c591293426cdb284e7c7cee6 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 25 Jan 2022 13:25:10 +0000 Subject: [PATCH] Unpickle package-private from TASTy Meaning package private things will be automatically filtered. --- .../typesafe/tools/mima/core/ClassInfo.scala | 3 +- .../typesafe/tools/mima/core/MemberInfo.scala | 3 +- .../tools/mima/core/MimaUnpickler.scala | 8 +- .../tools/mima/core/TastyFormat.scala | 3 +- .../tools/mima/core/TastyUnpickler.scala | 196 +++++++++++++----- .../tools/mima/lib/CollectProblemsTest.scala | 15 +- .../problems-3.txt | 1 - .../testAppRun-3.pending | 0 .../problems-3.txt | 1 - .../testAppRun-3.pending | 0 .../problems-3.txt | 1 - .../testAppRun-3.pending | 0 .../forwards-3.txt | 1 - .../testAppRun-3.pending | 0 .../problems-3.txt | 16 +- .../v1/A.scala | 76 +++---- .../problems-3.txt | 1 - .../testAppRun-3.pending | 0 .../problems-3.txt | 1 - .../testAppRun-3.pending | 0 .../problems-3.txt | 1 - .../testAppRun-3.pending | 0 .../problems-3.txt | 1 - .../testAppRun-3.pending | 0 .../problems-3.txt | 1 - .../testAppRun-3.pending | 0 .../problems-3.txt | 6 +- .../problems-3.txt | 2 - .../testAppRun-3.pending | 0 .../problems-3.txt | 1 - .../testAppRun-3.pending | 0 project/MimaSettings.scala | 1 + 32 files changed, 197 insertions(+), 142 deletions(-) delete mode 100644 functional-tests/src/test/method-with-package-private-overload-changes-nok/problems-3.txt delete mode 100644 functional-tests/src/test/method-with-package-private-overload-changes-nok/testAppRun-3.pending delete mode 100644 functional-tests/src/test/package-private-class-becomes-ok/problems-3.txt delete mode 100644 functional-tests/src/test/package-private-class-becomes-ok/testAppRun-3.pending delete mode 100644 functional-tests/src/test/package-private-class-changes-ok/problems-3.txt delete mode 100644 functional-tests/src/test/package-private-class-changes-ok/testAppRun-3.pending delete mode 100644 functional-tests/src/test/package-private-class-unbecomes-ok/forwards-3.txt delete mode 100644 functional-tests/src/test/package-private-class-unbecomes-ok/testAppRun-3.pending delete mode 100644 functional-tests/src/test/package-private-field-changes-ok/problems-3.txt delete mode 100644 functional-tests/src/test/package-private-field-changes-ok/testAppRun-3.pending delete mode 100644 functional-tests/src/test/package-private-method-changes-ok/problems-3.txt delete mode 100644 functional-tests/src/test/package-private-method-changes-ok/testAppRun-3.pending delete mode 100644 functional-tests/src/test/package-private-method-on-case-class-changes-ok/problems-3.txt delete mode 100644 functional-tests/src/test/package-private-method-on-case-class-changes-ok/testAppRun-3.pending delete mode 100644 functional-tests/src/test/package-private-method-on-nested-class-changes-ok/problems-3.txt delete mode 100644 functional-tests/src/test/package-private-method-on-nested-class-changes-ok/testAppRun-3.pending delete mode 100644 functional-tests/src/test/package-private-method-with-public-overload-changes-ok/problems-3.txt delete mode 100644 functional-tests/src/test/package-private-method-with-public-overload-changes-ok/testAppRun-3.pending delete mode 100644 functional-tests/src/test/package-private-object-changes-ok/problems-3.txt delete mode 100644 functional-tests/src/test/package-private-object-changes-ok/testAppRun-3.pending delete mode 100644 functional-tests/src/test/package-private-trait-changes-ok/problems-3.txt delete mode 100644 functional-tests/src/test/package-private-trait-changes-ok/testAppRun-3.pending diff --git a/core/src/main/scala/com/typesafe/tools/mima/core/ClassInfo.scala b/core/src/main/scala/com/typesafe/tools/mima/core/ClassInfo.scala index 2b3310e2c..7f7b4e0a8 100644 --- a/core/src/main/scala/com/typesafe/tools/mima/core/ClassInfo.scala +++ b/core/src/main/scala/com/typesafe/tools/mima/core/ClassInfo.scala @@ -87,7 +87,8 @@ private[mima] sealed abstract class ClassInfo(val owner: PackageInfo) extends In final def isInterface: Boolean = ClassfileParser.isInterface(flags) // java interface or trait w/o impl methods final def isClass: Boolean = !isTrait && !isInterface // class, object or trait's impl class - final def accessModifier: String = if (isProtected) "protected" else if (isPrivate) "private" else "" + final def scopedPrivateSuff: String = if (isScopedPrivate) "[..]" else "" + final def accessModifier: String = if (isProtected) s"protected$scopedPrivateSuff" else if (isPrivate) s"private$scopedPrivateSuff" else "" final def declarationPrefix: String = if (isModuleClass) "object" else if (isTrait) "trait" else if (isInterface) "interface" else "class" final lazy val fullName: String = if (owner.isRoot) bytecodeName else s"${owner.fullName}.$bytecodeName" final def formattedFullName: String = formatClassName(if (isModuleClass) fullName.init else fullName) diff --git a/core/src/main/scala/com/typesafe/tools/mima/core/MemberInfo.scala b/core/src/main/scala/com/typesafe/tools/mima/core/MemberInfo.scala index 6a17ddfb2..5886c6e85 100644 --- a/core/src/main/scala/com/typesafe/tools/mima/core/MemberInfo.scala +++ b/core/src/main/scala/com/typesafe/tools/mima/core/MemberInfo.scala @@ -15,6 +15,7 @@ sealed abstract class MemberInfo(val owner: ClassInfo, val bytecodeName: String, final def fullName: String = s"${owner.formattedFullName}.$decodedName" final def abstractPrefix = if (isDeferred && !owner.isTrait) "abstract " else "" + final def scopedPrivatePrefix = if (scopedPrivate) "private[..] " else "" final def staticPrefix: String = if (isStatic) "static " else "" final def tpe: Type = owner.owner.definitions.fromDescriptor(descriptor) final def hasSyntheticName: Boolean = decodedName.contains('$') @@ -43,7 +44,7 @@ private[mima] final class MethodInfo(owner: ClassInfo, bytecodeName: String, fla def shortMethodString: String = { val prefix = if (hasSyntheticName) if (isExtensionMethod) "extension " else "synthetic " else "" val deprecated = if (isDeprecated) "deprecated " else "" - s"${abstractPrefix}$prefix${deprecated}${staticPrefix}method $decodedName$tpe" + s"${scopedPrivatePrefix}${abstractPrefix}$prefix${deprecated}${staticPrefix}method $decodedName$tpe" } lazy val paramsCount: Int = { diff --git a/core/src/main/scala/com/typesafe/tools/mima/core/MimaUnpickler.scala b/core/src/main/scala/com/typesafe/tools/mima/core/MimaUnpickler.scala index e52f347db..bcd9f1086 100644 --- a/core/src/main/scala/com/typesafe/tools/mima/core/MimaUnpickler.scala +++ b/core/src/main/scala/com/typesafe/tools/mima/core/MimaUnpickler.scala @@ -18,9 +18,9 @@ object MimaUnpickler { val index = buf.createIndex val entries = new Array[Entry](index.length) val classes = new scala.collection.mutable.HashMap[SymInfo, ClassInfo] - def nnSyms = entries.iterator.collect { case s: SymbolInfo => s } - def defnSyms = nnSyms.filter(sym => sym.tag == CLASSsym || sym.tag == MODULEsym) - def methSyms = nnSyms.filter(sym => sym.tag == VALsym) + def syms = entries.iterator.collect { case s: SymbolInfo => s } + def defnSyms = syms.filter(sym => sym.tag == CLASSsym || sym.tag == MODULEsym) + def methSyms = syms.filter(sym => sym.tag == VALsym) def until[T](end: Int, op: () => T): List[T] = if (buf.readIndex == end) Nil else op() :: until(end, op) @@ -177,7 +177,7 @@ object MimaUnpickler { .filter(_.name.value != CONSTRUCTOR) // TODO support package private constructors .toSeq.groupBy(_.name).foreach { case (name, pickleMethods) => doMethodOverloads(clazz, name, pickleMethods) - } + } } def doMethodOverloads(clazz: ClassInfo, name: Name, pickleMethods: Seq[SymbolInfo]) = { diff --git a/core/src/main/scala/com/typesafe/tools/mima/core/TastyFormat.scala b/core/src/main/scala/com/typesafe/tools/mima/core/TastyFormat.scala index 98fe1e8a0..a1f28f21a 100644 --- a/core/src/main/scala/com/typesafe/tools/mima/core/TastyFormat.scala +++ b/core/src/main/scala/com/typesafe/tools/mima/core/TastyFormat.scala @@ -173,7 +173,7 @@ object TastyFormat { * ANNOTATEDtype Length underlying_Type annotation_Term -- underlying @ annotation * ANDtype Length left_Type right_Type -- left & right * ORtype Length left_Type right_Type -- lefgt | right - * MATCHtype Length bound_Type sel_Type case_Type* -- sel match {cases} with optional upper `bound` + * MATCHtype Length bound_Type sel_Type case_Type* -- sel match {cases} with optional upper `bound` * MATCHCASEtype Length pat_type rhs_Type -- match cases are MATCHCASEtypes or TYPELAMBDAtypes over MATCHCASEtypes * BIND Length boundName_NameRef bounds_Type Modifier* -- boundName @ bounds, for type-variables defined in a type pattern * BYNAMEtype underlying_Type -- => underlying @@ -181,7 +181,6 @@ object TastyFormat { * POLYtype Length result_Type TypesNames -- A polymorphic method type `[TypesNames]result`, used in refinements * METHODtype Length result_Type TypesNames Modifier* -- A method type `(Modifier* TypesNames)result`, needed for refinements, with optional modifiers for the parameters * TYPELAMBDAtype Length result_Type TypesNames -- A type lambda `[TypesNames] => result` - * SHAREDtype type_ASTRef -- link to previously serialized type * TypesNames = TypeName* * TypeName = typeOrBounds_ASTRef paramName_NameRef -- (`termName`: `type`) or (`typeName` `bounds`) * diff --git a/core/src/main/scala/com/typesafe/tools/mima/core/TastyUnpickler.scala b/core/src/main/scala/com/typesafe/tools/mima/core/TastyUnpickler.scala index 3add12e7e..68de8477a 100644 --- a/core/src/main/scala/com/typesafe/tools/mima/core/TastyUnpickler.scala +++ b/core/src/main/scala/com/typesafe/tools/mima/core/TastyUnpickler.scala @@ -10,6 +10,7 @@ import TastyFormat._, NameTags._, TastyTagOps._, TastyRefs._ object TastyUnpickler { def unpickleClass(in: TastyReader, clazz: ClassInfo, path: String): Unit = { val doPrint = false + //val doPrint = true //val doPrint = path.contains("v1") && !path.contains("exclude.tasty") //if (doPrint) TastyPrinter.printClassNames(in.fork, path) if (doPrint) TastyPrinter.printPickle(in.fork, path) @@ -18,69 +19,111 @@ object TastyUnpickler { val names = readNames(in) val tree = unpickleTree(getTreeReader(in, names), names) - copyAnnotations(tree, clazz) + copyPrivateWithin(tree, clazz.owner) + copyAnnotations(tree, clazz.owner) } - def copyAnnotations(tree: Tree, clazz: ClassInfo): Unit = { - new Traverser { - var pkgNames = List.empty[Name] - var clsNames = List.empty[Name] + private abstract class ClassTraverser(pkgInfo: PackageInfo) extends Traverser { + var pkgNames = List.empty[Name] + var clsNames = List.empty[Name] - override def traversePkg(pkg: Pkg): Unit = { - pkgNames ::= getPkgName(pkg) - super.traversePkg(pkg) - pkgNames = pkgNames.tail - } + override def traversePkg(pkg: Pkg): Unit = { + pkgNames ::= getPkgName(pkg) + super.traversePkg(pkg) + pkgNames = pkgNames.tail + } - override def traverseClsDef(clsDef: ClsDef): Unit = { - forEachClass(clsDef, pkgNames, clsNames) - clsNames ::= clsDef.name - super.traverseClsDef(clsDef) - clsNames = clsNames.tail - } - }.traverse(tree) + override def traverseClsDef(clsDef: ClsDef): Unit = { + clsNames ::= clsDef.name + val cls = currentClass + if (cls != NoClass) forEachClass(clsDef, cls) + super.traverseClsDef(clsDef) + clsNames = clsNames.tail + } def getPkgName(pkg: Pkg) = pkg.path match { case TypeRefPkg(fullyQual) => fullyQual case p: UnknownPath => SimpleName(p.show) } - def forEachClass(clsDef: ClsDef, pkgNames: List[Name], clsNames: List[Name]): Unit = { + def currentClass: ClassInfo = { val pkgName = pkgNames.headOption.getOrElse(nme.Empty) - if (pkgName.source == clazz.owner.fullName) { - val clsName = (clsDef.name :: clsNames).reverseIterator.mkString("$") - val cls = clazz.owner.classes.getOrElse(clsName, NoClass) - if (cls != NoClass) { - cls._annotations ++= clsDef.annots.map(annot => AnnotInfo(annot.tycon.toString)) - - for (defDef <- clsDef.template.meths) { - val annots = defDef.annots.map(annot => AnnotInfo(annot.tycon.toString)) - for (meth <- cls.lookupClassMethods(new MethodInfo(cls, defDef.name.source, 0, "()V"))) - meth._annotations ++= annots - } + if (pkgName.source == pkgInfo.fullName) { + val clsName0 = clsNames.reverseIterator.mkString("$") + val clsName = clsNames match { + case TypeName(ObjectName(_)) :: _ => clsName0 + "$" + case _ => clsName0 } - } + pkgInfo.classes.getOrElse(clsName, NoClass) + } else NoClass } + + def forEachClass(clsDef: ClsDef, cls: ClassInfo): Unit } + def copyAnnotations(tree: Tree, pkgInfo: PackageInfo): Unit = new ClassTraverser(pkgInfo) { + def forEachClass(clsDef: ClsDef, cls: ClassInfo): Unit = { + cls._annotations ++= clsDef.annots.map(annot => AnnotInfo(annot.tycon.toString)) + + for (defDef <- clsDef.template.meths) { + val annots = defDef.annots.map(annot => AnnotInfo(annot.tycon.toString)) + for (meth <- cls.lookupClassMethods(new MethodInfo(cls, defDef.name.source, 0, "()V"))) + meth._annotations ++= annots + } + } + }.traverse(tree) + + def copyPrivateWithin(tree: Tree, pkgInfo: PackageInfo): Unit = new ClassTraverser(pkgInfo) { + override def forEachClass(clsDef: ClsDef, cls: ClassInfo): Unit = + clsDef.privateWithin.foreach(_ => cls.module._scopedPrivate = true) + + override def traverseTemplate(tmpl: Template): Unit = { + super.traverseTemplate(tmpl) + doMethods(tmpl) + } + + def doMethods(tmpl: Template) = { + val clazz = currentClass + (tmpl.fields ::: tmpl.meths).iterator + .filter(_.name != Name.Constructor) // TODO support package private constructors + .toSeq.groupBy(_.name).foreach { case (name, pickleMethods) => + doMethodOverloads(clazz, name, pickleMethods) + } + } + + def doMethodOverloads(clazz: ClassInfo, name: Name, pickleMethods: Seq[TermMemberDef]) = { + val bytecodeMethods = clazz.methods.get(name.source).filter(!_.isBridge).toList + if (pickleMethods.size == bytecodeMethods.size && pickleMethods.exists(_.privateWithin.isDefined)) { + bytecodeMethods.zip(pickleMethods).foreach { case (bytecodeMeth, pickleMeth) => + bytecodeMeth.scopedPrivate = pickleMeth.privateWithin.isDefined + } + } + } + }.traverse(tree) + def unpickleTree(in: TastyReader, names: Names): Tree = { import in._ def readName() = names(readNat()) def skipTree(tag: Int) = { skipTreeTagged(in, tag); UnknownTree(tag) } - def readTypeRefPkg() = TypeRefPkg(readName()) // fullyQualified_NameRef -- A reference to a package member with given fully qualified name - def readTypeRef() = TypeRef(name = readName(), qual = readType()) // NameRef qual_Type -- A reference `qual.name` to a non-local member - def readAnnot() = { readEnd(); Annot(readType(), skipTree(readByte())) } // tycon_Type fullAnnotation_Tree -- An annotation, given (class) type of constructor, and full application tree - def readSharedType() = unpickleTree(forkAt(readAddr()), names).asInstanceOf[Type] + def readTypeRefPkg() = TypeRefPkg(readName()) // fullyQualified_NameRef -- A reference to a package member with given fully qualified name + def readTypeRef() = TypeRef(name = readName(), qual = readType()) // NameRef qual_Type -- A reference `qual.name` to a non-local member + def readAnnot() = { readEnd(); Annot(readType(), skipTree(readByte())) } // tycon_Type fullAnnotation_Tree -- An annotation, given (class) type of constructor, and full application tree + def readSharedType() = unpickleTree(forkAt(readAddr()), names) match { + case t @ UnknownTree(tag) => throw new Exception(s"Expected Type, but was UnknownTree(${astTagToString(tag)}); init:${t.stack.map("\n\tat " + _).mkString}\n") + case tree => tree.asInstanceOf[Type] + } def readPath() = readByte() match { case TERMREFpkg => readTypeRefPkg() + case TYPEREFpkg => readTypeRefPkg() case tag => skipTree(tag); UnknownPath(tag) } def readType(): Type = readByte() match { - case TYPEREF => readTypeRef() case TERMREFpkg => readTypeRefPkg() + case TYPEREFpkg => readTypeRefPkg() + case TYPEREF => readTypeRef() case SHAREDtype => readSharedType() case tag => skipTree(tag); UnknownType(tag) } @@ -95,6 +138,16 @@ object TastyUnpickler { def nothingButMods(end: Addr) = currentAddr == end || isModifierTag(nextByte) + def readValDef() = { + // Length NameRef type_Term rhs_Term? Modifier* -- modifiers val name : type (= rhs)? + val end = readEnd() + val name = readName() + skipTree(readByte()) // type + if (!nothingButMods(end)) skipTree(readByte()) // rhs + val (privateWithin, annots) = readMods(end) + ValDef(name, privateWithin, annots) + } + def readDefDef() = { // Length NameRef Param* returnType_Term rhs_Term? Modifier* -- modifiers def name [typeparams] paramss : returnType (= rhs)? // Param = TypeParam | TermParam @@ -103,8 +156,8 @@ object TastyUnpickler { while (nextByte == TYPEPARAM || nextByte == PARAM || nextByte == EMPTYCLAUSE || nextByte == SPLITCLAUSE) skipTree(readByte()) // params skipTree(readByte()) // returnType if (!nothingButMods(end)) skipTree(readByte()) // rhs - val annots = readAnnotsInMods(end) - DefDef(name, annots) + val (privateWithin, annots) = readMods(end) + DefDef(name, privateWithin, annots) } def readTemplate(): Template = { @@ -121,27 +174,39 @@ object TastyUnpickler { while (nextByte != SELFDEF && nextByte != DEFDEF) skipTree(readByte()) // parents if (nextByte == SELFDEF) skipTree(readByte()) // self val classes = new ListBuffer[ClsDef] + val fields = new ListBuffer[ValDef] val meths = new ListBuffer[DefDef] doUntil(end)(readByte() match { case TYPEDEF => readTypeDef() match { case clsDef: ClsDef => classes += clsDef case _ => } - case DEFDEF => meths += readDefDef() + case VALDEF => fields += readValDef() + case DEFDEF => meths += readDefDef() case tag => skipTree(tag) }) - Template(classes.toList, meths.toList) + Template(classes.toList, fields.toList, meths.toList) } - def readClassDef(name: Name, end: Addr) = ClsDef(name.toTypeName, readTemplate(), readAnnotsInMods(end)) // NameRef Template Modifier* -- modifiers class name template + def readClassDef(name: Name, end: Addr) = { + // NameRef Template Modifier* -- modifiers class name template + val template = readTemplate() + val (privateWithin, annots) = readMods(end) + ClsDef(name.toTypeName, template, privateWithin, annots) + } def readTypeMemberDef(end: Addr) = { goto(end); UnknownTree(TYPEDEF) } // NameRef type_Term Modifier* -- modifiers type name (= type | bounds) - def readAnnotsInMods(end: Addr) = { + def readMods(end: Addr): (Option[Type], List[Annot]) = { + // PRIVATEqualified qualifier_Type -- private[qualifier] + // PROTECTEDqualified qualifier_Type -- protected[qualifier] + var privateWithin = Option.empty[Type] val annots = new ListBuffer[Annot] doUntil(end)(readByte() match { case ANNOTATION => annots += readAnnot() + case PRIVATEqualified => privateWithin = Some(readType()) + case PROTECTEDqualified => privateWithin = Some(readType()) case tag if isModifierTag(tag) => skipTree(tag) - case tag => assert(false, s"illegal modifier tag $tag at ${currentAddr.index - 1}, end = $end") + case tag => assert(false, s"illegal modifier tag ${astTagToString(tag)} at ${currentAddr.index - 1}, end = $end") }) - annots.toList + (privateWithin, annots.toList) } def readTypeDef() = { @@ -164,12 +229,14 @@ object TastyUnpickler { case AstCat1TagOnly => skipTree(tag) case AstCat2Nat => tag match { case TERMREFpkg => readTypeRefPkg() + case TYPEREFpkg => readTypeRefPkg() case _ => skipTree(tag) } case AstCat3AST => readTree() case AstCat4NatAST => tag match { - case TYPEREF => readTypeRef() - case _ => skipTree(tag) + case TYPEREFsymbol => skipTree(tag) match { case UnknownTree(tag) => UnknownType(tag) } + case TYPEREF => readTypeRef() + case _ => skipTree(tag) } case AstCat5Length => processLengthTree() } @@ -192,6 +259,7 @@ object TastyUnpickler { sealed trait Tree extends ShowSelf final case class UnknownTree(tag: Int) extends Tree { + val stack = new Exception().getStackTrace val id = unknownTreeId.getAndIncrement() def show = s"UnknownTree(${astTagToString(tag)})" //+ s"#$id" } @@ -199,9 +267,17 @@ object TastyUnpickler { final case class Pkg(path: Path, trees: List[Tree]) extends Tree { def show = s"package $path${trees.map("\n " + _).mkString}" } - final case class ClsDef(name: TypeName, template: Template, annots: List[Annot]) extends Tree { def show = s"${annots.map("" + _ + " ").mkString}class $name$template" } - final case class Template(classes: List[ClsDef], meths: List[DefDef]) extends Tree { def show = s"${(classes ::: meths).map("\n " + _).mkString}" } - final case class DefDef(name: Name, annots: List[Annot] = Nil) extends Tree { def show = s"${annots.map("" + _ + " ").mkString}def $name" } + final case class ClsDef(name: TypeName, template: Template, privateWithin: Option[Type], annots: List[Annot]) extends Tree { + def show = s"${showXs(annots, end = " ")}${showPrivateWithin(privateWithin)}class $name$template" + } + final case class Template(classes: List[ClsDef], fields: List[ValDef], meths: List[DefDef]) extends Tree { def show = s"${(classes ::: meths).map("\n " + _).mkString}" } + final case class ValDef(name: Name, privateWithin: Option[Type], annots: List[Annot] = Nil) extends TermMemberDef + final case class DefDef(name: Name, privateWithin: Option[Type], annots: List[Annot] = Nil) extends TermMemberDef + + sealed trait TermMemberDef extends Tree { + def name: Name; def privateWithin: Option[Type]; def annots: List[Annot] + def show = s"${showXs(annots, end = " ")}${showPrivateWithin(privateWithin)}def $name" + } sealed trait Type extends Tree final case class UnknownType(tag: Int) extends Type { def show = s"UnknownType(${astTagToString(tag)})" } @@ -213,11 +289,17 @@ object TastyUnpickler { final case class Annot(tycon: Type, fullAnnotation: Tree) extends Tree { def show = s"@$tycon" } + def showPrivateWithin(privateWithin: Option[Type]): String = privateWithin match { + case Some(privateWithin) => s"private[${privateWithin.show}] " + case _ => "" + } + sealed class Traverser { def traverse(tree: Tree): Unit = tree match { case pkg: Pkg => traversePkg(pkg) case clsDef: ClsDef => traverseClsDef(clsDef) case tmpl: Template => traverseTemplate(tmpl) + case valDef: ValDef => traverseValDef(valDef) case defDef: DefDef => traverseDefDef(defDef) case tp: Type => traverseType(tp) case annot: Annot => traverseAnnot(annot) @@ -226,11 +308,13 @@ object TastyUnpickler { def traverseName(name: Name) = () - def traversePkg(pkg: Pkg) = { val Pkg(path, trees) = pkg; traverse(path); trees.foreach(traverse) } - def traverseClsDef(clsDef: ClsDef) = { val ClsDef(name, tmpl, annots) = clsDef; traverseName(name); traverseTemplate(tmpl); annots.foreach(traverse) } - def traverseTemplate(tmpl: Template) = { val Template(classes, meths) = tmpl; classes.foreach(traverse); meths.foreach(traverse) } - def traverseDefDef(defDef: DefDef) = { val DefDef(name, annots) = defDef; traverseName(name); annots.foreach(traverse) } - def traverseAnnot(annot: Annot) = { val Annot(tycon, fullAnnotation) = annot; traverse(tycon); traverse(fullAnnotation) } + def traversePkg(pkg: Pkg) = { val Pkg(path, trees) = pkg; traverse(path); trees.foreach(traverse) } + def traverseClsDef(clsDef: ClsDef) = { val ClsDef(name, tmpl, privateWithin, annots) = clsDef; traverseName(name); traverseTemplate(tmpl); traversePrivateWithin(privateWithin); annots.foreach(traverse) } + def traverseTemplate(tmpl: Template) = { val Template(classes, fields, meths) = tmpl; classes.foreach(traverse); fields.foreach(traverse); meths.foreach(traverse) } + def traverseValDef(valDef: ValDef) = { val ValDef(name, privateWithin, annots) = valDef; traverseName(name); traversePrivateWithin(privateWithin); annots.foreach(traverse) } + def traverseDefDef(defDef: DefDef) = { val DefDef(name, privateWithin, annots) = defDef; traverseName(name); traversePrivateWithin(privateWithin); annots.foreach(traverse) } + def traversePrivateWithin(privateWithin: Option[Type]) = { privateWithin.foreach(traverseType) } + def traverseAnnot(annot: Annot) = { val Annot(tycon, fullAnnotation) = annot; traverse(tycon); traverse(fullAnnotation) } def traversePath(path: Path) = path match { case TypeRefPkg(fullyQual) => traverseName(fullyQual) @@ -473,7 +557,7 @@ object TastyUnpickler { val uuid: UUID, ) - def readHeader(in: TastyReader) = { + def readHeader(in: TastyReader): Header = { import in._ def readToolingVersion() = { @@ -495,4 +579,8 @@ object TastyUnpickler { def show: String override def toString = show } + + def showXs[X <: ShowSelf](xs: List[X], start: String = "", sep: String = " ", end: String = ""): String = + if (xs.isEmpty) "" + else xs.iterator.map(_.show).mkString(start, sep, end) } diff --git a/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/CollectProblemsTest.scala b/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/CollectProblemsTest.scala index fd1dc0be1..f89dd6135 100644 --- a/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/CollectProblemsTest.scala +++ b/functional-tests/src/main/scala/com/typesafe/tools/mima/lib/CollectProblemsTest.scala @@ -6,7 +6,7 @@ import java.nio.file.Files import com.typesafe.tools.mima.core.ProblemFilter import scala.collection.JavaConverters._ -import scala.util.{ Failure, Success, Try } +import scala.util.{Failure, Success, Try} object CollectProblemsTest { def testCollectProblems(testCase: TestCase, direction: Direction): Try[Unit] = for { @@ -35,15 +35,20 @@ object CollectProblemsTest { val reported = problems.map(_.description(affectedVersion)) val msg = new StringBuilder("\n") - def pp(start: String, lines: List[String]) = + import scala.io.AnsiColor._ + final case class DiffType(sign: Char, colour: String) + val add = DiffType('+', GREEN) + val del = DiffType('-', RED) + + def pp(start: String, dt: DiffType, lines: List[String]) = if (lines.nonEmpty) { msg.append(s"$start (${lines.size}):") - lines.sorted.distinct.map("\n - " + _).foreach(msg.append(_)) + lines.sorted.distinct.map("\n" + dt.colour + dt.sign + _ + RESET).foreach(msg.append) msg.append("\n") } - pp("The following problem(s) were expected but not reported", expected.diff(reported)) - pp("The following problem(s) were reported but not expected", reported.diff(expected)) + pp("The following problem(s) were expected but not reported", del, expected.diff(reported)) + pp("The following problem(s) were reported but not expected", add, reported.diff(expected)) msg.mkString match { case "\n" => Success(()) diff --git a/functional-tests/src/test/method-with-package-private-overload-changes-nok/problems-3.txt b/functional-tests/src/test/method-with-package-private-overload-changes-nok/problems-3.txt deleted file mode 100644 index 1b67ec269..000000000 --- a/functional-tests/src/test/method-with-package-private-overload-changes-nok/problems-3.txt +++ /dev/null @@ -1 +0,0 @@ -method bar(Int)Int in class foo.Foo does not have a correspondent in new version diff --git a/functional-tests/src/test/method-with-package-private-overload-changes-nok/testAppRun-3.pending b/functional-tests/src/test/method-with-package-private-overload-changes-nok/testAppRun-3.pending deleted file mode 100644 index e69de29bb..000000000 diff --git a/functional-tests/src/test/package-private-class-becomes-ok/problems-3.txt b/functional-tests/src/test/package-private-class-becomes-ok/problems-3.txt deleted file mode 100644 index 1b67ec269..000000000 --- a/functional-tests/src/test/package-private-class-becomes-ok/problems-3.txt +++ /dev/null @@ -1 +0,0 @@ -method bar(Int)Int in class foo.Foo does not have a correspondent in new version diff --git a/functional-tests/src/test/package-private-class-becomes-ok/testAppRun-3.pending b/functional-tests/src/test/package-private-class-becomes-ok/testAppRun-3.pending deleted file mode 100644 index e69de29bb..000000000 diff --git a/functional-tests/src/test/package-private-class-changes-ok/problems-3.txt b/functional-tests/src/test/package-private-class-changes-ok/problems-3.txt deleted file mode 100644 index 1b67ec269..000000000 --- a/functional-tests/src/test/package-private-class-changes-ok/problems-3.txt +++ /dev/null @@ -1 +0,0 @@ -method bar(Int)Int in class foo.Foo does not have a correspondent in new version diff --git a/functional-tests/src/test/package-private-class-changes-ok/testAppRun-3.pending b/functional-tests/src/test/package-private-class-changes-ok/testAppRun-3.pending deleted file mode 100644 index e69de29bb..000000000 diff --git a/functional-tests/src/test/package-private-class-unbecomes-ok/forwards-3.txt b/functional-tests/src/test/package-private-class-unbecomes-ok/forwards-3.txt deleted file mode 100644 index e9b8d9342..000000000 --- a/functional-tests/src/test/package-private-class-unbecomes-ok/forwards-3.txt +++ /dev/null @@ -1 +0,0 @@ -method bar(Int,Int)Int in class foo.Foo does not have a correspondent in other version diff --git a/functional-tests/src/test/package-private-class-unbecomes-ok/testAppRun-3.pending b/functional-tests/src/test/package-private-class-unbecomes-ok/testAppRun-3.pending deleted file mode 100644 index e69de29bb..000000000 diff --git a/functional-tests/src/test/package-private-deeply-nested-ok/problems-3.txt b/functional-tests/src/test/package-private-deeply-nested-ok/problems-3.txt index 86cd2aaf7..d86c77bbc 100644 --- a/functional-tests/src/test/package-private-deeply-nested-ok/problems-3.txt +++ b/functional-tests/src/test/package-private-deeply-nested-ok/problems-3.txt @@ -1,17 +1,5 @@ -method go11()Int in object foo.l1.x has a different result type in new version, where it is java.lang.String rather than Int -method go12()Int in class foo.l1.x has a different result type in new version, where it is java.lang.String rather than Int -method go21()Int in object foo.l2a.x#y has a different result type in new version, where it is java.lang.String rather than Int -method go22()Int in class foo.l2a.x#y has a different result type in new version, where it is java.lang.String rather than Int -method go23()Int in object foo.l2b.x#y has a different result type in new version, where it is java.lang.String rather than Int -method go24()Int in class foo.l2b.x#y has a different result type in new version, where it is java.lang.String rather than Int -method go31()Int in object foo.l3a.x#y#z has a different result type in new version, where it is java.lang.String rather than Int -method go32()Int in class foo.l3a.x#y#z has a different result type in new version, where it is java.lang.String rather than Int -method go33()Int in object foo.l3b.x#y#z has a different result type in new version, where it is java.lang.String rather than Int -method go34()Int in class foo.l3b.x#y#z has a different result type in new version, where it is java.lang.String rather than Int -method go35()Int in object foo.l3c.x#y#z has a different result type in new version, where it is java.lang.String rather than Int -method go36()Int in class foo.l3c.x#y#z has a different result type in new version, where it is java.lang.String rather than Int -method go37()Int in object foo.l3d.x#y#z has a different result type in new version, where it is java.lang.String rather than Int -method go38()Int in class foo.l3d.x#y#z has a different result type in new version, where it is java.lang.String rather than Int +# privateWithin isn't honoured in static method emission in Scala 3 +# https://github.com/lampepfl/dotty/issues/10842 static method go11()Int in class foo.l1.x has a different result type in new version, where it is java.lang.String rather than Int static method go21()Int in class foo.l2a.x#y has a different result type in new version, where it is java.lang.String rather than Int static method go31()Int in class foo.l3a.x#y#z has a different result type in new version, where it is java.lang.String rather than Int diff --git a/functional-tests/src/test/package-private-deeply-nested-ok/v1/A.scala b/functional-tests/src/test/package-private-deeply-nested-ok/v1/A.scala index a57ef045e..fafcca0af 100644 --- a/functional-tests/src/test/package-private-deeply-nested-ok/v1/A.scala +++ b/functional-tests/src/test/package-private-deeply-nested-ok/v1/A.scala @@ -7,63 +7,49 @@ package foo // Because of scala/bug#2034 we can't put these all in one package (you get "name clash" errors) // So instead we'll split them in 4 nice and even packages package l1 { object x { private[foo] def go11() = 11 }; class x { private[foo] def go12() = 12 }} -// l1/x.class: -// 0: MODULEsym x 8 -// 1: CLASSsym x 8 -// 4: CLASSsym x 8 -// MODULEsym + CLASSsym x // x + x$ -// VALsym go11 private[foo] -// CLASSsym x // x -// VALsym go12 private[foo] +// l1/x.tasty: +// VALDEF x + TYPEDEF x[ModuleClass] // x + x$ +// TYPEDEF x // x package l2a { object x { object y { private[foo] def go21() = 21 }; class y { private[foo] def go22() = 22 }}} package l2b { class x { object y { private[foo] def go23() = 23 }; class y { private[foo] def go24() = 24 }}} -// l2a/x.class: -// 0 MODULEsym x 11 -// 1 CLASSsym x 11 -// 3 MODULEsym y 1 -// 4 CLASSsym y 1 -// 7 CLASSsym y 1 -// MODULEsym + CLASSsym x // x + x$ -// MODULEsym + CLASSsym y // x$y$ -// CLASSsym y // x$y +// l2a/x.tasty: +// VALDEF x + TYPEDEF x[ModuleClass] // x + x$ +// VALDEF y + TYPEDEF y[ModuleClass] // x$y$ +// TYPEDEF y // x$y -// l2b/x.class: -// 0 CLASSsym x 10 -// 2 MODULEsym y 0 -// 3 CLASSsym y 0 -// 6 CLASSsym y 0 -// CLASSsym x // x -// MODULEsym + CLASSsym y // x$y$ -// CLASSsym y // x$y +// l2b/x.tasty: +// TYPEDEF x // x +// VALDEF y + TYPEDEF y[ModuleClass] // x$y$ +// TYPEDEF y // x$y package l3a { object x { object y { object z { private[foo] def go31() = 31 }; class z { private[foo] def go32() = 32 }}}} package l3b { object x { class y { object z { private[foo] def go33() = 33 }; class z { private[foo] def go34() = 34 }}}} package l3c { class x { object y { object z { private[foo] def go35() = 35 }; class z { private[foo] def go36() = 36 }}}} package l3d { class x { class y { object z { private[foo] def go37() = 37 }; class z { private[foo] def go38() = 38 }}}} -// l3a/x.class: -// 0 MODULEsym x 14 + 1 CLASSsym x 14 // x + x$ -// 3 MODULEsym y 1 + 4 CLASSsym y 1 // x$y$ -// 6 MODULEsym z 4 + 7 CLASSsym z 4 // x$y$z$ -// 10 CLASSsym z 4 // x$y$z +// l3a/x.tasty: +// VALDEF x + TYPEDEF x[ModuleClass] // x + x$ +// VALDEF y + TYPEDEF y[ModuleClass] // x$y$ +// VALDEF z + TYPEDEF z[ModuleClass] // x$y$z$ +// TYPEDEF z // x$y$z // -// l3b/x.class: -// 0 MODULEsym x 13 + 1 CLASSsym x 13 // x + x$ -// 3 CLASSsym y 1 // x$y -// 5 MODULEsym z 3 + 6 CLASSsym z 3 // x$y$z$ -// 9 CLASSsym z 3 // x$y$z +// l3b/x.tasty: +// VALDEF x + TYPEDEF 5 x[ModuleClass] // x + x$ +// TYPEDEF y // x$y +// VALDEF z + TYPEDEF z[ModuleClass] // x$y$z$ +// TYPEDEF z // x$y$z // -// l3c/x.class: -// 0 CLASSsym x 13 // x -// 2 MODULEsym y 0 + 3 CLASSsym y 0 // x$y$ -// 5 MODULEsym z 3 + 6 CLASSsym z 3 // x$y$z$ -// 9 CLASSsym z 3 // x$y$z$ +// l3c/x.tasty: +// TYPEDEF x // x +// VALDEF y + TYPEDEF y[ModuleClass] // x$y$ +// VALDEF z + TYPEDEF z[ModuleClass] // x$y$z$ +// TYPEDEF z // x$y$z // -// l3d/x.class: -// 0 CLASSsym x 12 // x -// 2 CLASSsym y 0 // x$y -// 4 MODULEsym z 2 + 5 CLASSsym z 2 // x$y$z$ -// 8 CLASSsym z 2 // x$y$z +// l3d/x.tasty: +// TYPEDEF x // x +// TYPEDEF y // x$y +// VALDEF z + TYPEDEF z[ModuleClass] // x$y$z$ +// TYPEDEF z // x$y$z object Lib { def doIt = { doL1(); doL2(); doL3() } diff --git a/functional-tests/src/test/package-private-field-changes-ok/problems-3.txt b/functional-tests/src/test/package-private-field-changes-ok/problems-3.txt deleted file mode 100644 index d367d6737..000000000 --- a/functional-tests/src/test/package-private-field-changes-ok/problems-3.txt +++ /dev/null @@ -1 +0,0 @@ -method bar()Int in class foo.Foo has a different result type in new version, where it is java.lang.String rather than Int diff --git a/functional-tests/src/test/package-private-field-changes-ok/testAppRun-3.pending b/functional-tests/src/test/package-private-field-changes-ok/testAppRun-3.pending deleted file mode 100644 index e69de29bb..000000000 diff --git a/functional-tests/src/test/package-private-method-changes-ok/problems-3.txt b/functional-tests/src/test/package-private-method-changes-ok/problems-3.txt deleted file mode 100644 index 1b67ec269..000000000 --- a/functional-tests/src/test/package-private-method-changes-ok/problems-3.txt +++ /dev/null @@ -1 +0,0 @@ -method bar(Int)Int in class foo.Foo does not have a correspondent in new version diff --git a/functional-tests/src/test/package-private-method-changes-ok/testAppRun-3.pending b/functional-tests/src/test/package-private-method-changes-ok/testAppRun-3.pending deleted file mode 100644 index e69de29bb..000000000 diff --git a/functional-tests/src/test/package-private-method-on-case-class-changes-ok/problems-3.txt b/functional-tests/src/test/package-private-method-on-case-class-changes-ok/problems-3.txt deleted file mode 100644 index 1b67ec269..000000000 --- a/functional-tests/src/test/package-private-method-on-case-class-changes-ok/problems-3.txt +++ /dev/null @@ -1 +0,0 @@ -method bar(Int)Int in class foo.Foo does not have a correspondent in new version diff --git a/functional-tests/src/test/package-private-method-on-case-class-changes-ok/testAppRun-3.pending b/functional-tests/src/test/package-private-method-on-case-class-changes-ok/testAppRun-3.pending deleted file mode 100644 index e69de29bb..000000000 diff --git a/functional-tests/src/test/package-private-method-on-nested-class-changes-ok/problems-3.txt b/functional-tests/src/test/package-private-method-on-nested-class-changes-ok/problems-3.txt deleted file mode 100644 index e9e8bd773..000000000 --- a/functional-tests/src/test/package-private-method-on-nested-class-changes-ok/problems-3.txt +++ /dev/null @@ -1 +0,0 @@ -method bar(Int)Int in class foo.Foo#Bar does not have a correspondent in new version diff --git a/functional-tests/src/test/package-private-method-on-nested-class-changes-ok/testAppRun-3.pending b/functional-tests/src/test/package-private-method-on-nested-class-changes-ok/testAppRun-3.pending deleted file mode 100644 index e69de29bb..000000000 diff --git a/functional-tests/src/test/package-private-method-with-public-overload-changes-ok/problems-3.txt b/functional-tests/src/test/package-private-method-with-public-overload-changes-ok/problems-3.txt deleted file mode 100644 index 1b67ec269..000000000 --- a/functional-tests/src/test/package-private-method-with-public-overload-changes-ok/problems-3.txt +++ /dev/null @@ -1 +0,0 @@ -method bar(Int)Int in class foo.Foo does not have a correspondent in new version diff --git a/functional-tests/src/test/package-private-method-with-public-overload-changes-ok/testAppRun-3.pending b/functional-tests/src/test/package-private-method-with-public-overload-changes-ok/testAppRun-3.pending deleted file mode 100644 index e69de29bb..000000000 diff --git a/functional-tests/src/test/package-private-methods-on-class-and-companion-change-ok/problems-3.txt b/functional-tests/src/test/package-private-methods-on-class-and-companion-change-ok/problems-3.txt index e60324403..30bce9f9c 100644 --- a/functional-tests/src/test/package-private-methods-on-class-and-companion-change-ok/problems-3.txt +++ b/functional-tests/src/test/package-private-methods-on-class-and-companion-change-ok/problems-3.txt @@ -1,5 +1,3 @@ -method bar(Int)Int in class foo.Foo does not have a correspondent in new version -method baz(Int)Int in object foo.Foo does not have a correspondent in new version -method qux(Int)Int in class foo.Foo does not have a correspondent in new version -method qux(Int)Int in object foo.Foo does not have a correspondent in new version +# I guess privateWithin isn't honoured in static method emission in Scala 3 +# https://github.com/lampepfl/dotty/issues/10842 static method baz(Int)Int in class foo.Foo does not have a correspondent in new version diff --git a/functional-tests/src/test/package-private-object-changes-ok/problems-3.txt b/functional-tests/src/test/package-private-object-changes-ok/problems-3.txt deleted file mode 100644 index 57fba9fb3..000000000 --- a/functional-tests/src/test/package-private-object-changes-ok/problems-3.txt +++ /dev/null @@ -1,2 +0,0 @@ -method bar(Int)Int in object foo.Foo does not have a correspondent in new version -static method bar(Int)Int in class foo.Foo does not have a correspondent in new version diff --git a/functional-tests/src/test/package-private-object-changes-ok/testAppRun-3.pending b/functional-tests/src/test/package-private-object-changes-ok/testAppRun-3.pending deleted file mode 100644 index e69de29bb..000000000 diff --git a/functional-tests/src/test/package-private-trait-changes-ok/problems-3.txt b/functional-tests/src/test/package-private-trait-changes-ok/problems-3.txt deleted file mode 100644 index d136073be..000000000 --- a/functional-tests/src/test/package-private-trait-changes-ok/problems-3.txt +++ /dev/null @@ -1 +0,0 @@ -method bar(Int)Int in interface foo.Foo does not have a correspondent in new version diff --git a/functional-tests/src/test/package-private-trait-changes-ok/testAppRun-3.pending b/functional-tests/src/test/package-private-trait-changes-ok/testAppRun-3.pending deleted file mode 100644 index e69de29bb..000000000 diff --git a/project/MimaSettings.scala b/project/MimaSettings.scala index c0cfd41cd..5ca24b3fc 100644 --- a/project/MimaSettings.scala +++ b/project/MimaSettings.scala @@ -24,6 +24,7 @@ object MimaSettings { // * com.typesafe.tools.mima.core.ProblemFilters // * com.typesafe.tools.mima.core.*Problem // * com.typesafe.tools.mima.core.util.log.Logging + exclude[Problem]("com.typesafe.tools.mima.core.TastyUnpickler*"), ), ) }