Skip to content

Commit

Permalink
Make hashing childrenOfSealedClass stable
Browse files Browse the repository at this point in the history
The order of childrenOfSealedClass isn't stable, so use hashSymmetric
over hashArray to avoid over-compilation (due to false hash changes).
  • Loading branch information
dwijnand committed May 27, 2020
1 parent 45edf10 commit 0567500
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 11 deletions.
4 changes: 3 additions & 1 deletion internal/zinc-apiinfo/src/main/scala/xsbt/api/HashAPI.scala
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ final class HashAPI private (
hashTypeParameters(c.typeParameters)
hashType(c.selfType)
if (includeSealedChildren)
hashTypes(c.childrenOfSealedClass, includeDefinitions)
hashTypesSymmetric(c.childrenOfSealedClass, includeDefinitions)

val isTrait = c.definitionType() == DefinitionType.Trait
hashStructure(c.structure, includeDefinitions, isTrait)
Expand Down Expand Up @@ -341,6 +341,8 @@ final class HashAPI private (

def hashTypes(ts: Array[Type], includeDefinitions: Boolean = true) =
hashArray(ts, (t: Type) => hashType(t, includeDefinitions))
def hashTypesSymmetric(ts: Array[Type], includeDefinitions: Boolean = true) =
hashSymmetric(ts, hashType(_, includeDefinitions))
def hashType(t: Type, includeDefinitions: Boolean = true): Unit =
t match {
case s: Structure => hashStructure(s, includeDefinitions, false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@

package xsbt.api

import xsbti.api.{ ClassLike, Def, DefinitionType, Var, Modifiers, TypeDeclaration, TypeAlias }
import xsbti.api._
import sbt.internal.inc.UnitSpec
import xsbt.api.ClassLikeHelpers._

class HashAPISpecification extends UnitSpec {
"The HashAPI specification" should "detect hash changes in private trait vars" in {
behavior of "The HashAPI specification"

it should "detect hash changes in private trait vars" in {
val privateVar = Var.of("x", privateAccess, defaultMods, Array.empty, emptyType)
val tWithPrivateMember = simpleTrait("Foo", List(privateVar))
val tWithNothing = simpleTrait("Foo", Nil)
Expand Down Expand Up @@ -85,18 +87,26 @@ class HashAPISpecification extends UnitSpec {
assertDifferentPrivateAPI(tWithPrivateMember, tWithNothing)
}

it should "not detect hash changes in childrenOfSealedClass ordering" in {
val t = Projection.of(emptyType, "t")
val f = Projection.of(emptyType, "f")
val b = simpleTrait("Bool", Nil)
val x = b.withChildrenOfSealedClass(Array(f, t))
val y = b.withChildrenOfSealedClass(Array(t, f))
assertSamePrivateAPI(x, y) // was "-188164889 did not equal 992093599"
}

def assertDifferentPrivateAPI(a: ClassLike, b: ClassLike): Unit = assertPrivateApi(true, a, b)
def assertSamePrivateAPI(a: ClassLike, b: ClassLike): Unit = assertPrivateApi(false, a, b)
def assertPrivateApi(isDifferent: Boolean, a: ClassLike, b: ClassLike): Unit = {
def hashWithPrivate(c: ClassLike): Int =
HashAPI.apply(_.hashAPI(c), includePrivateDefsInTrait = true)

def PrivateAPI(c: ClassLike): Int = HashAPI(_.hashAPI(c), includePrivateDefsInTrait = true)
def checkOrder(a: ClassLike, b: ClassLike): Unit = {
// Check the the default Same API doesn't take private methods into account
assert(HashAPI.apply(a) == HashAPI.apply(b), s"HashAPI(${a}) != HashAPI(${b})")
val samePrivateHash = hashWithPrivate(a) == hashWithPrivate(b)
if (isDifferent) assert(!samePrivateHash, s"PrivateAPI(${a}) == PrivateAPI(${b})")
else assert(samePrivateHash, s"PrivateAPI(${a}) != PrivateAPI(${b})")
// Check the default Hash API doesn't take private methods into account
assert(HashAPI(a) == HashAPI(b), s"HashAPI(${a}) != HashAPI(${b})")
if (isDifferent)
assert(PrivateAPI(a) != PrivateAPI(b), s"PrivateAPI(${a}) == PrivateAPI(${b})")
else
assert(PrivateAPI(a) == PrivateAPI(b), s"PrivateAPI(${a}) != PrivateAPI(${b})")
()
}

Expand Down

0 comments on commit 0567500

Please sign in to comment.