Merge branch '0.13.10' into wip/0.13.10-merge

This commit is contained in:
Eugene Yokota
2016-02-22 13:19:38 -05:00
41 changed files with 405 additions and 198 deletions
@@ -261,7 +261,7 @@ object ClassToAPI {
def modifiers(i: Int): api.Modifiers =
{
import Modifier.{ isAbstract, isFinal }
new api.Modifiers(isAbstract(i), false, isFinal(i), false, false, false, false)
new api.Modifiers(isAbstract(i), false, isFinal(i), false, false, false, false, false)
}
def access(i: Int, pkg: Option[String]): api.Access =
{
@@ -12,7 +12,7 @@ object APIUtil {
}
val byteToModifiers = (b: Byte) => {
def x(bit: Int) = (b & (1 << bit)) != 0
new Modifiers(x(0), x(1), x(2), x(3), x(4), x(5), x(6))
new Modifiers(x(0), x(1), x(2), x(3), x(4), x(5), x(6), x(7))
}
def isScalaSourceName(name: String): Boolean = name.endsWith(".scala")
@@ -139,16 +139,37 @@ final class HashAPI(includePrivate: Boolean, includeParamNames: Boolean, include
{
hash = startHash(0)
hashSymmetric(s.packages, hashPackage)
hashDefinitions(s.definitions, true)
hashDefinitions(s.definitions, topLevel = true, isTrait = false)
finalizeHash
}
def hashPackage(p: Package) = hashString(p.name)
@deprecated("Use the overload that indicates if the enclosing definition is a trait.", "0.14")
def hashDefinitions(ds: Seq[Definition], topLevel: Boolean): Unit =
hashDefinitions(ds, topLevel, isTrait = false)
def hashDefinitions(ds: Seq[Definition], topLevel: Boolean, isTrait: Boolean): Unit =
{
def isPublic(d: Definition): Boolean = d.access match { case _: xsbti.api.Public => true; case _ => false }
def isTraitBreaker(d: Definition): Boolean = d match {
// Vars and vals in traits introduce getters, setters and fields in the implementing classes.
// See test `source-dependencies/trait-private-var
case _: FieldLike => true
// Objects in traits introduce fields in the implementing classes.
// See test `source-dependencies/trait-private-object`
case cl: ClassLike => cl.definitionType == DefinitionType.Module
// super calls introduce accessors that are not part of the public API
case d: Def => d.modifiers.isSuperAccessor
case _ => false
}
val includedPrivateDefinitions =
if (!includePrivate && !topLevel && isTrait) {
ds filter (x => isTraitBreaker(x) && !isPublic(x))
} else Seq.empty
val defs = SameAPI.filterDefinitions(ds, topLevel, includePrivate)
hashSymmetric(defs, hashDefinition)
hashSymmetric(includedPrivateDefinitions ++ defs, hashDefinition)
}
/**
@@ -356,9 +377,9 @@ final class HashAPI(includePrivate: Boolean, includeParamNames: Boolean, include
def hashStructure0(structure: Structure, includeDefinitions: Boolean, isTrait: Boolean = false): Unit = {
extend(StructureHash)
hashTypes(structure.parents, includeDefinitions)
if (includeDefinitions || isTrait) {
hashDefinitions(structure.declared, isTrait)
hashDefinitions(structure.inherited, isTrait)
if (includeDefinitions) {
hashDefinitions(structure.declared, topLevel = false, isTrait)
hashDefinitions(structure.inherited, topLevel = false, isTrait)
}
}
def hashParameters(parameters: Seq[TypeParameter], base: Type): Unit =
@@ -165,20 +165,39 @@ class NameHashingSpecification extends Specification {
}
/**
* Checks that private members are included in the hash of the public API of traits.
* Including the private members of traits is required because classes that implement a trait
* have to define the private members of the trait. Therefore, if a private member of a trait is added,
* modified or removed we need to recompile the classes that implement this trait.
* Checks that private vars are included in the hash of the public API of traits.
* Including the private vars of traits is required because classes that implement a trait
* have to define getters and setters for these vars.
* For instance, if trait Foo is initially defined as:
* trait Foo { private val x = new A }
* trait Foo { private var x = new A }
* changing it to
* trait Foo { private val x = new B }
* trait Foo { private var x = new B }
* requires us to recompile all implementors of trait Foo, because scalac generates setters and getters
* for the private fields of trait Foo in its implementor. If the clients of trait Foo are not recompiled,
* for the private vars of trait Foo in its implementor. If the clients of trait Foo are not recompiled,
* we get abstract method errors at runtime, because the types expected by the setter (for instance) does not
* match.
*/
"private members in traits" in {
"private var in traits are included in API hash" in {
/* trait Foo { private var x } */
val fooTrait1 =
simpleTrait("Foo",
simpleStructure(new Var(emptyType, "x", privateAccess, defaultModifiers, Array.empty)),
publicAccess)
/* trait Foo */
val fooTrait2 =
simpleTrait("Foo",
simpleStructure(),
publicAccess)
val api1 = new SourceAPI(Array.empty, Array(fooTrait1))
val api2 = new SourceAPI(Array.empty, Array(fooTrait2))
HashAPI(api1) !== HashAPI(api2)
}
"private vals in traits are included in API hash" in {
/* trait Foo { private val x } */
val fooTrait1 =
simpleTrait("Foo",
@@ -198,15 +217,97 @@ class NameHashingSpecification extends Specification {
}
"private objects in traits are included in API hash" in {
/* trait Foo { private object x } */
val fooTrait1 =
simpleTrait("Foo",
simpleStructure(
new ClassLike(DefinitionType.Module, lzy(emptyType), lzy(simpleStructure()), Array.empty, Array.empty, "x", privateAccess, defaultModifiers, Array.empty)),
publicAccess)
/* trait Foo */
val fooTrait2 =
simpleTrait("Foo",
simpleStructure(),
publicAccess)
val api1 = new SourceAPI(Array.empty, Array(fooTrait1))
val api2 = new SourceAPI(Array.empty, Array(fooTrait2))
HashAPI(api1) !== HashAPI(api2)
}
"private non-synthetic def in traits are not included in API hash" in {
/* trait Foo { private def x } */
val fooTrait1 =
simpleTrait("Foo",
simpleStructure(new Def(Array.empty, emptyType, Array.empty, "x", privateAccess, defaultModifiers, Array.empty)),
publicAccess)
/* trait Foo */
val fooTrait2 =
simpleTrait("Foo",
simpleStructure(),
publicAccess)
val api1 = new SourceAPI(Array.empty, Array(fooTrait1))
val api2 = new SourceAPI(Array.empty, Array(fooTrait2))
HashAPI(api1) === HashAPI(api2)
}
"private synthetic def in traits are included in API hash" in {
/* trait Foo { private <superaccessor> def x } */
val modifiers = new xsbti.api.Modifiers(false, false, false, false, false, false, false, true)
val fooTrait1 =
simpleTrait("Foo",
simpleStructure(new Def(Array.empty, emptyType, Array.empty, "x", privateAccess, modifiers, Array.empty)),
publicAccess)
/* trait Foo */
val fooTrait2 =
simpleTrait("Foo",
simpleStructure(),
publicAccess)
val api1 = new SourceAPI(Array.empty, Array(fooTrait1))
val api2 = new SourceAPI(Array.empty, Array(fooTrait2))
HashAPI(api1) !== HashAPI(api2)
}
"private types in traits are included not in API hash" in {
/* trait Foo { private type x } */
val fooTrait1 =
simpleTrait("Foo",
simpleStructure(new TypeAlias(emptyType, Array.empty, "x", privateAccess, defaultModifiers, Array.empty)),
publicAccess)
/* trait Foo */
val fooTrait2 =
simpleTrait("Foo",
simpleStructure(),
publicAccess)
val api1 = new SourceAPI(Array.empty, Array(fooTrait1))
val api2 = new SourceAPI(Array.empty, Array(fooTrait2))
HashAPI(api1) === HashAPI(api2)
}
/**
* Checks that private members in non-top-level traits are included as well.
* Checks that private vars in non-top-level traits are included as well.
*/
"private members in nested traits" in {
/* class A { trait Foo { private val x } } */
"private variables in nested traits are include in the API hash" in {
/* class A { trait Foo { private var x } } */
val classA1 =
simpleClass("A",
simpleTrait("Foo",
simpleStructure(new Val(emptyType, "x", privateAccess, defaultModifiers, Array.empty)),
simpleStructure(new Var(emptyType, "x", privateAccess, defaultModifiers, Array.empty)),
publicAccess))
/* class A { trait Foo } */
@@ -226,12 +327,12 @@ class NameHashingSpecification extends Specification {
/**
* Checks that private traits are NOT included in the hash.
*/
"private traits" in {
/* class Foo { private trait T { private val x } } */
"private inner traits are not included in the API hash" in {
/* class Foo { private trait T { private var x } } */
val classFoo1 =
simpleClass("Foo",
simpleTrait("T",
simpleStructure(new Val(emptyType, "x", privateAccess, defaultModifiers, Array.empty)),
simpleStructure(new Var(emptyType, "x", privateAccess, defaultModifiers, Array.empty)),
privateAccess))
/** class Foo { private trait T } */
@@ -256,10 +357,10 @@ class NameHashingSpecification extends Specification {
* Checks that private members are NOT included in the hash of the public API of classes.
*/
"private members in classes are not included in the api hash" in {
/* class Foo { private val x } */
/* class Foo { private var x } */
val classFoo1 =
simpleClass("Foo",
simpleStructure(new Val(emptyType, "x", privateAccess, defaultModifiers, Array.empty)))
simpleStructure(new Var(emptyType, "x", privateAccess, defaultModifiers, Array.empty)))
/* class Foo */
val classFoo2 =
@@ -274,9 +375,9 @@ class NameHashingSpecification extends Specification {
}
/**
* Checks that private members do NOT contribute to name hashes.
* Test for https://github.com/sbt/sbt/issues/2324
*/
* Checks that private members do NOT contribute to name hashes.
* Test for https://github.com/sbt/sbt/issues/2324
*/
"private members in classes do not contribute to name hashes" in {
/* class Foo { private val x } */
val classFoo =
@@ -336,6 +437,6 @@ class NameHashingSpecification extends Specification {
private val strTpe = new Projection(emptyType, "String")
private val publicAccess = new Public
private val privateAccess = new Private(new Unqualified)
private val defaultModifiers = new Modifiers(false, false, false, false, false, false, false)
private val defaultModifiers = new Modifiers(false, false, false, false, false, false, false, false)
}