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 c8d240b
Show file tree
Hide file tree
Showing 15 changed files with 13 additions and 6 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
@@ -0,0 +1 @@
synthetic static method $init$(B)Unit in interface B does not have a correspondent in new version
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
synthetic static method $init$(B)Unit in interface B does not have a correspondent in new version
Empty file.

0 comments on commit c8d240b

Please sign in to comment.