Skip to content

Commit

Permalink
Report the loss of trait init methods
Browse files Browse the repository at this point in the history
In the encoding of traits the presence of a method with a body (a
"concrete method") triggers the generation of a trait initialisation
method, "$init$", which will be invoked by subclasses during
construction.  The effect this has is that dropping the last concrete
method(s) from a trait _removes_ the trait init method, which is a
binary incompatible change to client's subclasses.

Fixes lightbend-labs#426
  • Loading branch information
dwijnand committed Jan 13, 2020
1 parent a418260 commit 81a1d91
Show file tree
Hide file tree
Showing 16 changed files with 32 additions and 22 deletions.
11 changes: 6 additions & 5 deletions core/src/main/scala/com/typesafe/tools/mima/core/ClassInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,15 @@ sealed abstract class ClassInfo(val owner: PackageInfo) extends InfoLike with Eq
thisAndSuperClasses.flatMap(_.fields.get(field.bytecodeName))

final def lookupClassMethods(method: MethodInfo): Iterator[MethodInfo] = {
method.bytecodeName match {
case MemberInfo.ConstructorName => methods.get(MemberInfo.ConstructorName) // constructors are not inherited
case name => thisAndSuperClasses.flatMap(_.methods.get(name))
}
val name = method.bytecodeName
if (name == MemberInfo.ConstructorName) methods.get(name) // constructors are not inherited
else if (method.isStatic) methods.get(name) // static methods are not inherited
else thisAndSuperClasses.flatMap(_.methods.get(name))
}

private def lookupInterfaceMethods(method: MethodInfo): Iterator[MethodInfo] =
allInterfaces.iterator.flatMap(_.methods.get(method.bytecodeName))
if (method.isStatic) Iterator.empty // static methods are not inherited
else allInterfaces.iterator.flatMap(_.methods.get(method.bytecodeName))

final def lookupMethods(method: MethodInfo): Iterator[MethodInfo] =
lookupClassMethods(method) ++ lookupInterfaceMethods(method)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,17 @@ private[mima] final class MethodInfo(owner: ClassInfo, bytecodeName: String, fla
def matchesType(other: MethodInfo): Boolean = parametersDesc == other.parametersDesc

private def isDefaultGetter: Boolean = decodedName.contains("$default$")
private def isTraitInit: Boolean = decodedName == "$init$"
private def isExtensionMethod: Boolean = {
var i = decodedName.length - 1
while (i >= 0 && Character.isDigit(decodedName.charAt(i)))
i -= 1
decodedName.substring(0, i + 1).endsWith("$extension")
}
def nonAccessible: Boolean = !isPublic || isSynthetic || (hasSyntheticName && !isExtensionMethod && !isDefaultGetter)
def nonAccessible: Boolean = {
!isPublic || isSynthetic ||
(hasSyntheticName && !(isExtensionMethod || isDefaultGetter || isTraitInit))
}

override def toString = s"def $bytecodeName: $descriptor"
}
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
method foo()Int in interface B does not have a correspondent in new version
synthetic static method $init$(B)Unit in interface B does not have a correspondent in new version
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
trait A {
def foo: Int = 2
}



trait B extends A {
private def _B_init = ()
}

class C extends B

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
trait A {
def foo: Int = 2
}



trait B extends A {
private def _B_init = ()
}

This file was deleted.

0 comments on commit 81a1d91

Please sign in to comment.