2010-01-01 00:56:37 +01:00
|
|
|
/* sbt -- Simple Build Tool
|
2010-01-23 02:17:49 +01:00
|
|
|
* Copyright 2009, 2010 Mark Harrah
|
2010-01-01 00:56:37 +01:00
|
|
|
*/
|
|
|
|
|
package xsbt.api
|
|
|
|
|
|
|
|
|
|
import xsbti.api._
|
|
|
|
|
|
|
|
|
|
import Function.tupled
|
|
|
|
|
import scala.collection.{immutable, mutable}
|
|
|
|
|
|
2010-06-22 03:23:42 +02:00
|
|
|
class NameChanges(val newTypes: Set[String], val removedTypes: Set[String], val newTerms: Set[String], val removedTerms: Set[String])
|
2010-07-02 12:57:03 +02:00
|
|
|
{
|
|
|
|
|
override def toString =
|
|
|
|
|
(("New types", newTypes) :: ("Removed types", removedTypes) :: ("New terms", newTerms) :: ("Removed terms", removedTerms) :: Nil).map {
|
|
|
|
|
case (label,set) => label + ":\n\t" + set.mkString("\n\t")
|
|
|
|
|
}.mkString("Name changes:\n ", "\n ", "\n")
|
|
|
|
|
|
|
|
|
|
}
|
2010-06-22 03:23:42 +02:00
|
|
|
|
2010-01-24 06:12:47 +01:00
|
|
|
object TopLevel
|
|
|
|
|
{
|
|
|
|
|
/** Identifies removed and new top-level definitions by name. */
|
2010-06-22 03:23:42 +02:00
|
|
|
def nameChanges(a: Iterable[Source], b: Iterable[Source]): NameChanges =
|
2010-01-24 06:12:47 +01:00
|
|
|
{
|
|
|
|
|
def changes(s: Set[String], t: Set[String]) = (s -- t, t -- s)
|
|
|
|
|
|
|
|
|
|
val (avalues, atypes) = definitions(a)
|
|
|
|
|
val (bvalues, btypes) = definitions(b)
|
|
|
|
|
|
|
|
|
|
val (newTypes, removedTypes) = changes(names(atypes), names(btypes))
|
2010-06-22 03:23:42 +02:00
|
|
|
val (newTerms, removedTerms) = changes(names(avalues), names(bvalues))
|
2010-01-24 06:12:47 +01:00
|
|
|
|
2010-06-22 03:23:42 +02:00
|
|
|
new NameChanges(newTypes, removedTypes, newTerms, removedTerms)
|
2010-01-24 06:12:47 +01:00
|
|
|
}
|
2010-06-22 03:23:42 +02:00
|
|
|
def definitions(i: Iterable[Source]) = SameAPI.separateDefinitions(i.toSeq.flatMap( _.definitions ))
|
|
|
|
|
def names(s: Iterable[Definition]): Set[String] = Set() ++ s.map(_.name)
|
2010-01-24 06:12:47 +01:00
|
|
|
}
|
2010-10-23 03:55:16 +02:00
|
|
|
import TagTypeVariables.TypeVars
|
2010-01-24 06:12:47 +01:00
|
|
|
/** Checks the API of two source files for equality.*/
|
2010-01-01 00:56:37 +01:00
|
|
|
object SameAPI
|
|
|
|
|
{
|
|
|
|
|
def apply(a: Source, b: Source) =
|
2010-01-08 03:48:56 +01:00
|
|
|
{
|
|
|
|
|
val start = System.currentTimeMillis
|
2010-01-23 02:17:49 +01:00
|
|
|
|
2010-07-28 05:01:45 +02:00
|
|
|
/*println("\n=========== API #1 ================")
|
2010-01-23 02:17:49 +01:00
|
|
|
import DefaultShowAPI._
|
|
|
|
|
println(ShowAPI.show(a))
|
|
|
|
|
println("\n=========== API #2 ================")
|
2010-07-28 05:01:45 +02:00
|
|
|
println(ShowAPI.show(b))*/
|
2010-07-02 12:57:03 +02:00
|
|
|
|
|
|
|
|
/** de Bruijn levels for type parameters in source a and b*/
|
|
|
|
|
val tagsA = TagTypeVariables(a)
|
|
|
|
|
val tagsB = TagTypeVariables(b)
|
|
|
|
|
|
|
|
|
|
val result = (new SameAPI(tagsA,tagsB, false, true)).check(a,b)
|
2010-01-08 03:48:56 +01:00
|
|
|
val end = System.currentTimeMillis
|
|
|
|
|
println(" API comparison took: " + (end - start) / 1000.0 + " s")
|
|
|
|
|
result
|
|
|
|
|
}
|
2010-01-24 06:12:47 +01:00
|
|
|
|
|
|
|
|
def separateDefinitions(s: Seq[Definition]): (Seq[Definition], Seq[Definition]) =
|
2010-06-16 02:38:18 +02:00
|
|
|
s.partition(isValueDefinition)
|
2010-01-24 06:12:47 +01:00
|
|
|
def isValueDefinition(d: Definition): Boolean =
|
|
|
|
|
d match
|
|
|
|
|
{
|
|
|
|
|
case _: FieldLike | _: Def=> true
|
|
|
|
|
case c: ClassLike => isValue(c.definitionType)
|
|
|
|
|
case _ => false
|
|
|
|
|
}
|
|
|
|
|
def isValue(d: DefinitionType): Boolean =
|
|
|
|
|
d == DefinitionType.Module || d == DefinitionType.PackageModule
|
|
|
|
|
/** Puts the given definitions in a map according to their names.*/
|
2010-06-16 02:38:18 +02:00
|
|
|
def byName(s: Seq[Definition]): Map[String, List[Definition]] =
|
2010-01-24 06:12:47 +01:00
|
|
|
{
|
2010-06-16 02:38:18 +02:00
|
|
|
var map = Map[String, List[Definition]]()
|
2010-01-24 06:12:47 +01:00
|
|
|
for(d <- s; name = d.name)
|
2010-06-16 02:38:18 +02:00
|
|
|
map = map.updated(name, d :: map.getOrElse(name, Nil) )
|
|
|
|
|
map
|
2010-01-24 06:12:47 +01:00
|
|
|
}
|
2010-01-01 00:56:37 +01:00
|
|
|
}
|
2010-01-24 06:12:47 +01:00
|
|
|
/** Used to implement API equality. All comparisons must be done between constructs in source files `a` and `b`. For example, when doing:
|
|
|
|
|
* `sameDefinitions(as, bs)`, `as` must be definitions from source file `a` and `bs` must be definitions from source file `b`. This is in order
|
|
|
|
|
* to properly handle type parameters, which must be computed for each source file and then referenced during comparison.
|
|
|
|
|
*
|
|
|
|
|
* If `includePrivate` is true, `private` and `private[this]` members are included in the comparison. Otherwise, those members are excluded.
|
|
|
|
|
*/
|
2010-07-02 12:57:03 +02:00
|
|
|
class SameAPI(tagsA: TypeVars, tagsB: TypeVars, includePrivate: Boolean, includeParamNames: Boolean)
|
2010-01-01 00:56:37 +01:00
|
|
|
{
|
2010-01-24 06:12:47 +01:00
|
|
|
import SameAPI._
|
2010-10-23 03:55:16 +02:00
|
|
|
|
|
|
|
|
private val pending = new mutable.HashSet[(Structure, Structure)]
|
|
|
|
|
private[this] val debugEnabled = java.lang.Boolean.getBoolean("xsbt.api.debug")
|
2010-01-23 02:17:49 +01:00
|
|
|
def debug(flag: Boolean, msg: => String): Boolean =
|
2010-01-10 00:22:58 +01:00
|
|
|
{
|
2010-10-23 03:55:16 +02:00
|
|
|
if(debugEnabled && !flag) println(msg)
|
2010-01-10 00:22:58 +01:00
|
|
|
flag
|
|
|
|
|
}
|
2010-01-01 00:56:37 +01:00
|
|
|
|
2010-01-24 06:12:47 +01:00
|
|
|
/** Returns true if source `a` has the same API as source `b`.*/
|
2010-07-02 12:57:03 +02:00
|
|
|
def check(a: Source, b: Source): Boolean =
|
2010-01-10 00:22:58 +01:00
|
|
|
{
|
2010-01-23 02:17:49 +01:00
|
|
|
samePackages(a, b) &&
|
|
|
|
|
debug(sameDefinitions(a, b), "Definitions differed")
|
2010-01-10 00:22:58 +01:00
|
|
|
}
|
2010-01-01 00:56:37 +01:00
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def samePackages(a: Source, b: Source): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
sameStrings(packages(a), packages(b))
|
|
|
|
|
def packages(s: Source): Set[String] =
|
|
|
|
|
Set() ++ s.packages.map(_.name)
|
|
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameDefinitions(a: Source, b: Source): Boolean =
|
2010-01-24 06:12:47 +01:00
|
|
|
sameDefinitions(a.definitions, b.definitions, true)
|
|
|
|
|
def sameDefinitions(a: Seq[Definition], b: Seq[Definition], topLevel: Boolean): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
{
|
2010-01-24 06:12:47 +01:00
|
|
|
val (avalues, atypes) = separateDefinitions(filterDefinitions(a, topLevel))
|
|
|
|
|
val (bvalues, btypes) = separateDefinitions(filterDefinitions(b, topLevel))
|
2010-01-01 00:56:37 +01:00
|
|
|
debug(sameDefinitions(byName(avalues), byName(bvalues)), "Value definitions differed") &&
|
|
|
|
|
debug(sameDefinitions(byName(atypes), byName(btypes)), "Type definitions differed")
|
|
|
|
|
}
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameDefinitions(a: scala.collection.Map[String, List[Definition]], b: scala.collection.Map[String, List[Definition]]): Boolean =
|
|
|
|
|
debug(sameStrings(a.keySet, b.keySet), "\tDefinition strings differed") && zippedEntries(a,b).forall(tupled(sameNamedDefinitions))
|
2010-01-01 00:56:37 +01:00
|
|
|
|
2010-01-24 06:12:47 +01:00
|
|
|
/** Removes definitions that should not be considered for API equality.
|
|
|
|
|
* All top-level definitions are always considered: 'private' only means package-private.
|
|
|
|
|
* Other definitions are considered if they are not qualified with 'private[this]' or 'private'.*/
|
|
|
|
|
def filterDefinitions(d: Seq[Definition], topLevel: Boolean) = if(topLevel || includePrivate) d else d.filter(isNonPrivate)
|
2010-01-23 02:17:49 +01:00
|
|
|
def isNonPrivate(d: Definition): Boolean = isNonPrivate(d.access)
|
2010-01-24 06:12:47 +01:00
|
|
|
/** Returns false if the `access` is `Private` and qualified, true otherwise.*/
|
|
|
|
|
def isNonPrivate(access: Access): Boolean =
|
|
|
|
|
access match
|
2010-01-23 02:17:49 +01:00
|
|
|
{
|
2010-01-24 06:12:47 +01:00
|
|
|
case p: Private if !p.qualifier.isInstanceOf[IdQualifier] => false
|
2010-01-23 02:17:49 +01:00
|
|
|
case _ => true
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-24 06:12:47 +01:00
|
|
|
/** Checks that the definitions in `a` are the same as those in `b`, ignoring order.
|
|
|
|
|
* Each list is assumed to have already been checked to have the same names (by `sameDefinitions`, for example).*/
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameNamedDefinitions(a: List[Definition], b: List[Definition]): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
{
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameDefs(a: List[Definition], b: List[Definition]): Boolean =
|
2010-01-10 00:22:58 +01:00
|
|
|
{
|
2010-01-01 00:56:37 +01:00
|
|
|
a match
|
|
|
|
|
{
|
2010-01-10 00:22:58 +01:00
|
|
|
case adef :: atail =>
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameDef(seen: List[Definition], remaining: List[Definition]): Boolean =
|
2010-01-10 00:22:58 +01:00
|
|
|
remaining match
|
|
|
|
|
{
|
|
|
|
|
case Nil => debug(false, "Definition different in new API: \n" + adef.name )
|
|
|
|
|
case bdef :: btail =>
|
2010-01-23 02:17:49 +01:00
|
|
|
val eq = sameDefinitionContent(adef, bdef)
|
|
|
|
|
if(eq) sameDefs(atail, seen ::: btail) else sameDef(bdef :: seen, btail)
|
2010-01-10 00:22:58 +01:00
|
|
|
}
|
|
|
|
|
sameDef(Nil, b)
|
2010-01-23 02:17:49 +01:00
|
|
|
case Nil => true
|
2010-01-01 00:56:37 +01:00
|
|
|
}
|
2010-01-10 00:22:58 +01:00
|
|
|
}
|
2010-01-23 02:17:49 +01:00
|
|
|
debug((a.length == b.length), "\t\tLength differed for " + a.headOption.map(_.name).getOrElse("empty")) && sameDefs(a, b)
|
2010-01-01 00:56:37 +01:00
|
|
|
}
|
|
|
|
|
|
2010-01-24 06:12:47 +01:00
|
|
|
/** Checks that the two definitions are the same, other than their name.*/
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameDefinitionContent(a: Definition, b: Definition): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
//a.name == b.name &&
|
|
|
|
|
debug(sameAccess(a.access, b.access), "Access differed") &&
|
|
|
|
|
debug(sameModifiers(a.modifiers, b.modifiers), "Modifiers differed") &&
|
|
|
|
|
debug(sameAnnotations(a.annotations, b.annotations), "Annotations differed") &&
|
|
|
|
|
debug(sameDefinitionSpecificAPI(a, b), "Definition-specific differed")
|
|
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameAccess(a: Access, b: Access): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
(a, b) match
|
|
|
|
|
{
|
|
|
|
|
case (_: Public, _: Public) => true
|
|
|
|
|
case (qa: Protected, qb: Protected) => sameQualifier(qa, qb)
|
|
|
|
|
case (qa: Private, qb: Private) => sameQualifier(qa, qb)
|
|
|
|
|
case _ => debug(false, "Different access categories")
|
|
|
|
|
}
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameQualifier(a: Qualified, b: Qualified): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
sameQualifier(a.qualifier, b.qualifier)
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameQualifier(a: Qualifier, b: Qualifier): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
(a, b) match
|
|
|
|
|
{
|
|
|
|
|
case (_: Unqualified, _: Unqualified) => true
|
|
|
|
|
case (_: ThisQualifier, _: ThisQualifier) => true
|
|
|
|
|
case (ia: IdQualifier, ib: IdQualifier) => debug(ia.value == ib.value, "Different qualifiers")
|
|
|
|
|
case _ => debug(false, "Different qualifier categories: " + a.getClass.getName + " -- " +b.getClass.getName)
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameModifiers(a: Modifiers, b: Modifiers): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
bitSet(a) == bitSet(b)
|
|
|
|
|
|
|
|
|
|
def bitSet(m: Modifiers): immutable.BitSet =
|
|
|
|
|
{
|
|
|
|
|
import m._
|
|
|
|
|
val bs = new mutable.BitSet
|
|
|
|
|
setIf(bs, isAbstract, 0)
|
2010-10-23 03:55:16 +02:00
|
|
|
setIf(bs, isOverride, 1)
|
|
|
|
|
setIf(bs, isFinal, 2)
|
|
|
|
|
setIf(bs, isSealed, 3)
|
|
|
|
|
setIf(bs, isImplicit, 4)
|
|
|
|
|
setIf(bs, isLazy, 5)
|
2010-01-01 00:56:37 +01:00
|
|
|
bs.toImmutable
|
|
|
|
|
}
|
2010-01-23 02:17:49 +01:00
|
|
|
def setIf(bs: mutable.BitSet, flag: Boolean, i: Int): Unit =
|
|
|
|
|
if(flag) bs += i
|
2010-01-01 00:56:37 +01:00
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameAnnotations(a: Seq[Annotation], b: Seq[Annotation]): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
sameSeq(a, b)(sameAnnotation)
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameAnnotation(a: Annotation, b: Annotation): Boolean =
|
2010-01-06 01:50:43 +01:00
|
|
|
debug(sameSimpleType(a.base, b.base), "Annotation base type differed") &&
|
|
|
|
|
debug(sameAnnotationArguments(a.arguments, b.arguments), "Annotation arguments differed (" + a + ") and (" + b + ")")
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameAnnotationArguments(a: Seq[AnnotationArgument], b: Seq[AnnotationArgument]): Boolean =
|
2010-01-06 01:50:43 +01:00
|
|
|
argumentMap(a) == argumentMap(b)
|
|
|
|
|
def argumentMap(a: Seq[AnnotationArgument]): Map[String,String] =
|
|
|
|
|
Map() ++ a.map(arg => (arg.name, arg.value))
|
2010-01-01 00:56:37 +01:00
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameDefinitionSpecificAPI(a: Definition, b: Definition): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
(a, b) match
|
|
|
|
|
{
|
|
|
|
|
case (fa: FieldLike, fb: FieldLike) => sameFieldSpecificAPI(fa, fb)
|
|
|
|
|
case (pa: ParameterizedDefinition, pb: ParameterizedDefinition) => sameParameterizedDefinition(pa, pb)
|
|
|
|
|
case _ => false
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameParameterizedDefinition(a: ParameterizedDefinition, b: ParameterizedDefinition): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
debug(sameTypeParameters(a.typeParameters, b.typeParameters), "Different type parameters for " + a.name) &&
|
|
|
|
|
sameParameterizedSpecificAPI(a, b)
|
|
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameParameterizedSpecificAPI(a: ParameterizedDefinition, b: ParameterizedDefinition): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
(a, b) match
|
|
|
|
|
{
|
|
|
|
|
case (da: Def, db: Def) => sameDefSpecificAPI(da, db)
|
|
|
|
|
case (ca: ClassLike, cb: ClassLike) => sameClassLikeSpecificAPI(ca, cb)
|
|
|
|
|
case (ta: TypeAlias, tb: TypeAlias) => sameAliasSpecificAPI(ta, tb)
|
|
|
|
|
case (ta: TypeDeclaration, tb: TypeDeclaration) => sameDeclarationSpecificAPI(ta, tb)
|
|
|
|
|
case _ => false
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameDefSpecificAPI(a: Def, b: Def): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
debug(sameValueParameters(a.valueParameters, b.valueParameters), "Different def value parameters for " + a.name) &&
|
|
|
|
|
debug(sameType(a.returnType, b.returnType), "Different def return type for " + a.name)
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameAliasSpecificAPI(a: TypeAlias, b: TypeAlias): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
debug(sameType(a.tpe, b.tpe), "Different alias type for " + a.name)
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameDeclarationSpecificAPI(a: TypeDeclaration, b: TypeDeclaration): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
debug(sameType(a.lowerBound, b.lowerBound), "Different lower bound for declaration " + a.name) &&
|
|
|
|
|
debug(sameType(a.upperBound, b.upperBound), "Different upper bound for declaration " + a.name)
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameFieldSpecificAPI(a: FieldLike, b: FieldLike): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
debug(sameFieldCategory(a, b), "Different field categories (" + a.name + "=" + a.getClass.getName + " -- " +a.name + "=" + a.getClass.getName + ")")&&
|
|
|
|
|
debug(sameType(a.tpe, b.tpe), "Different field type for " + a.name)
|
|
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameFieldCategory(a: FieldLike, b: FieldLike): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
(a,b) match
|
|
|
|
|
{
|
|
|
|
|
case (_: Val, _: Val) => true
|
|
|
|
|
case (_: Var, _: Var) => true
|
|
|
|
|
case _=> false
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameClassLikeSpecificAPI(a: ClassLike, b: ClassLike): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
sameDefinitionType(a.definitionType, b.definitionType) &&
|
|
|
|
|
sameType(a.selfType, b.selfType) &&
|
|
|
|
|
sameStructure(a.structure, b.structure)
|
|
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameValueParameters(a: Seq[ParameterList], b: Seq[ParameterList]): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
sameSeq(a, b)(sameParameterList)
|
|
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameParameterList(a: ParameterList, b: ParameterList): Boolean =
|
|
|
|
|
(a.isImplicit == b.isImplicit) &&
|
2010-01-01 00:56:37 +01:00
|
|
|
sameParameters(a.parameters, b.parameters)
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameParameters(a: Seq[MethodParameter], b: Seq[MethodParameter]): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
sameSeq(a, b)(sameMethodParameter)
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameMethodParameter(a: MethodParameter, b: MethodParameter): Boolean =
|
2010-07-02 12:57:03 +02:00
|
|
|
(!includeParamNames || a.name == b.name) &&
|
2010-01-01 00:56:37 +01:00
|
|
|
sameType(a.tpe, b.tpe) &&
|
|
|
|
|
(a.hasDefault == b.hasDefault) &&
|
|
|
|
|
sameParameterModifier(a.modifier, b.modifier)
|
|
|
|
|
def sameParameterModifier(a: ParameterModifier, b: ParameterModifier) =
|
|
|
|
|
a == b
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameDefinitionType(a: DefinitionType, b: DefinitionType): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
a == b
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameVariance(a: Variance, b: Variance): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
a == b
|
|
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameTypeParameters(a: Seq[TypeParameter], b: Seq[TypeParameter]): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
debug(sameSeq(a, b)(sameTypeParameter), "Different type parameters")
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameTypeParameter(a: TypeParameter, b: TypeParameter): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
sameTypeParameters(a.typeParameters, b.typeParameters) &&
|
2010-01-23 02:17:49 +01:00
|
|
|
debug(sameAnnotations(a.annotations, b.annotations), "Different type parameter annotations") &&
|
2010-01-01 00:56:37 +01:00
|
|
|
debug(sameVariance(a.variance, b.variance), "Different variance") &&
|
|
|
|
|
debug(sameType(a.lowerBound, b.lowerBound), "Different lower bound") &&
|
|
|
|
|
debug(sameType(a.upperBound, b.upperBound), "Different upper bound") &&
|
2010-01-23 02:17:49 +01:00
|
|
|
debug(sameTags(a.id, b.id), "Different type parameter bindings")
|
|
|
|
|
|
|
|
|
|
def sameTags(a: Int, b: Int): Boolean = tagsA(a) == tagsB(b)
|
2010-01-01 00:56:37 +01:00
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameType(a: Type, b: Type): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
(a, b) match
|
|
|
|
|
{
|
2010-01-23 02:17:49 +01:00
|
|
|
case (sa: SimpleType, sb: SimpleType) => debug(sameSimpleType(sa, sb), "Different simple types: " + DefaultShowAPI(sa) + " and " + DefaultShowAPI(sb))
|
2010-01-10 00:22:58 +01:00
|
|
|
case (aa: Annotated, ab: Annotated) => debug(sameAnnotatedType(aa, ab), "Different annotated types")
|
2010-01-01 00:56:37 +01:00
|
|
|
case (sa: Structure, sb: Structure) => debug(sameStructure(sa, sb), "Different structure type")
|
|
|
|
|
case (ea: Existential, eb: Existential) => debug(sameExistentialType(ea, eb), "Different existential type")
|
2010-01-08 03:48:56 +01:00
|
|
|
case (pa: Polymorphic, pb: Polymorphic) => debug(samePolymorphicType(pa, pb), "Different polymorphic type")
|
2010-01-01 00:56:37 +01:00
|
|
|
case _ => false
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameExistentialType(a: Existential, b: Existential): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
sameTypeParameters(a.clause, b.clause) &&
|
|
|
|
|
sameType(a.baseType, b.baseType)
|
2010-01-23 02:17:49 +01:00
|
|
|
def samePolymorphicType(a: Polymorphic, b: Polymorphic): Boolean =
|
2010-01-08 03:48:56 +01:00
|
|
|
sameTypeParameters(a.parameters, b.parameters) &&
|
|
|
|
|
sameType(a.baseType, b.baseType)
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameAnnotatedType(a: Annotated, b: Annotated): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
sameSimpleType(a.baseType, b.baseType) &&
|
|
|
|
|
sameAnnotations(a.annotations, b.annotations)
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameStructure(a: Structure, b: Structure): Boolean =
|
2010-10-23 03:55:16 +02:00
|
|
|
if(pending add ((a,b)) )
|
|
|
|
|
try { sameStructureDirect(a,b) }
|
|
|
|
|
finally { pending -= ((a,b)) }
|
|
|
|
|
else
|
|
|
|
|
true
|
|
|
|
|
def sameStructureDirect(a: Structure, b: Structure): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
sameSeq(a.parents, b.parents)(sameType) &&
|
2010-01-23 02:17:49 +01:00
|
|
|
sameMembers(a.declared, b.declared) &&
|
2010-01-01 00:56:37 +01:00
|
|
|
sameMembers(a.inherited, b.inherited)
|
|
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameMembers(a: Seq[Definition], b: Seq[Definition]): Boolean =
|
2010-01-24 06:12:47 +01:00
|
|
|
sameDefinitions(a, b, false)
|
2010-01-01 00:56:37 +01:00
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameSimpleType(a: SimpleType, b: SimpleType): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
(a, b) match
|
|
|
|
|
{
|
|
|
|
|
case (pa: Projection, pb: Projection) => debug(sameProjection(pa, pb), "Different projection")
|
|
|
|
|
case (pa: ParameterRef, pb: ParameterRef) => debug(sameParameterRef(pa, pb), "Different parameter ref")
|
|
|
|
|
case (sa: Singleton, sb: Singleton) => debug(sameSingleton(sa, sb), "Different singleton")
|
|
|
|
|
case (_: EmptyType, _: EmptyType) => true
|
|
|
|
|
case (pa: Parameterized, pb: Parameterized) => debug(sameParameterized(pa, pb), "Different parameterized")
|
2010-01-06 01:50:43 +01:00
|
|
|
case _ => debug(false, "Different category of simple type (" + a.getClass.getName + " and " + b.getClass.getName + ") for (" + a + " and " + b + ")")
|
2010-01-01 00:56:37 +01:00
|
|
|
}
|
|
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameParameterized(a: Parameterized, b: Parameterized): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
sameSimpleType(a.baseType, b.baseType) &&
|
2010-01-08 03:48:56 +01:00
|
|
|
sameSeq(a.typeArguments, b.typeArguments)(sameType)
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameParameterRef(a: ParameterRef, b: ParameterRef): Boolean = sameTags(a.id, b.id)
|
|
|
|
|
def sameSingleton(a: Singleton, b: Singleton): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
samePath(a.path, b.path)
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameProjection(a: Projection, b: Projection): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
sameSimpleType(a.prefix, b.prefix) &&
|
|
|
|
|
(a.id == b.id)
|
|
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def samePath(a: Path, b: Path): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
samePathComponents(a.components, b.components)
|
2010-01-23 02:17:49 +01:00
|
|
|
def samePathComponents(a: Seq[PathComponent], b: Seq[PathComponent]): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
sameSeq(a, b)(samePathComponent)
|
2010-01-23 02:17:49 +01:00
|
|
|
def samePathComponent(a: PathComponent, b: PathComponent): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
(a, b) match
|
|
|
|
|
{
|
|
|
|
|
case (_: This, _: This) => true
|
|
|
|
|
case (sa: Super, sb: Super) => samePathSuper(sa, sb)
|
|
|
|
|
case (ia: Id, ib: Id) => samePathId(ia, ib)
|
|
|
|
|
case _ => false
|
|
|
|
|
}
|
2010-01-23 02:17:49 +01:00
|
|
|
def samePathSuper(a: Super, b: Super): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
samePath(a.qualifier, b.qualifier)
|
2010-01-23 02:17:49 +01:00
|
|
|
def samePathId(a: Id, b: Id): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
a.id == b.id
|
|
|
|
|
|
|
|
|
|
// precondition: a.keySet == b.keySet
|
|
|
|
|
protected def zippedEntries[A,B](a: scala.collection.Map[A,B], b: scala.collection.Map[A,B]): Iterable[(B,B)] =
|
|
|
|
|
for( (key, avalue) <- a) yield (avalue, b(key))
|
|
|
|
|
|
2010-01-23 02:17:49 +01:00
|
|
|
def sameStrings(a: scala.collection.Set[String], b: scala.collection.Set[String]): Boolean =
|
2010-01-01 00:56:37 +01:00
|
|
|
a == b
|
2010-01-23 02:17:49 +01:00
|
|
|
final def sameSeq[T](a: Seq[T], b: Seq[T])(eq: (T,T) => Boolean): Boolean =
|
|
|
|
|
(a.length == b.length) && (a zip b).forall(tupled(eq))
|
2010-01-01 00:56:37 +01:00
|
|
|
}
|