mirror of https://github.com/sbt/sbt.git
Merge pull request #2343 from sbt/topic/inc-comp-stable-self
Assorted incremental compiler bug fixes.
This commit is contained in:
commit
f9dc203247
|
|
@ -5,284 +5,147 @@ package xsbt.api
|
|||
|
||||
import xsbti.api._
|
||||
|
||||
trait Show[A] {
|
||||
def show(a: A): String
|
||||
}
|
||||
import scala.util.Try
|
||||
|
||||
final class ShowLazy[A](delegate: => Show[A]) extends Show[A] {
|
||||
private lazy val s = delegate
|
||||
def show(a: A) = s.show(a)
|
||||
}
|
||||
object DefaultShowAPI {
|
||||
private lazy val defaultNesting = Try { java.lang.Integer.parseInt(sys.props.get("sbt.inc.apidiff.depth").get) } getOrElse 2
|
||||
|
||||
import ShowAPI._
|
||||
def apply(d: Definition) = ShowAPI.showDefinition(d)(defaultNesting)
|
||||
def apply(d: Type) = ShowAPI.showType(d)(defaultNesting)
|
||||
def apply(a: SourceAPI) = ShowAPI.showApi(a)(defaultNesting)
|
||||
}
|
||||
|
||||
object ShowAPI {
|
||||
def Show[T](implicit s: Show[T]): Show[T] = s
|
||||
def show[T](t: T)(implicit s: Show[T]): String = s.show(t)
|
||||
private lazy val numDecls = Try { java.lang.Integer.parseInt(sys.props.get("sbt.inc.apidiff.decls").get) } getOrElse 0
|
||||
|
||||
def bounds(lower: Type, upper: Type)(implicit t: Show[Type]): String =
|
||||
">: " + t.show(lower) + " <: " + t.show(upper)
|
||||
private def truncateDecls(decls: Array[Definition]): Array[Definition] = if (numDecls <= 0) decls else decls.take(numDecls)
|
||||
private def lines(ls: Seq[String]): String = ls.mkString("\n", "\n", "\n")
|
||||
|
||||
import ParameterModifier._
|
||||
def parameterModifier(base: String, pm: ParameterModifier): String =
|
||||
pm match {
|
||||
case Plain => base
|
||||
case Repeated => base + "*"
|
||||
case ByName => "=> " + base
|
||||
def showApi(a: SourceAPI)(implicit nesting: Int) =
|
||||
a.packages.map(pkg => "package " + pkg.name).mkString("\n") + lines(truncateDecls(a.definitions).map(showDefinition))
|
||||
|
||||
def showDefinition(d: Definition)(implicit nesting: Int): String = d match {
|
||||
case v: Val => showMonoDef(v, "val") + ": " + showType(v.tpe)
|
||||
case v: Var => showMonoDef(v, "var") + ": " + showType(v.tpe)
|
||||
case d: Def => showPolyDef(d, "def") + showValueParams(d.valueParameters) + ": " + showType(d.returnType)
|
||||
case ta: TypeAlias => showPolyDef(ta, "type") + " = " + showType(ta.tpe)
|
||||
case td: TypeDeclaration => showPolyDef(td, "type") + showBounds(td.lowerBound, td.upperBound)
|
||||
case cl: ClassLike => showPolyDef(cl, showDefinitionType(cl.definitionType)) + " extends " + showTemplate(cl)
|
||||
}
|
||||
|
||||
private def showTemplate(cl: ClassLike)(implicit nesting: Int) =
|
||||
if (nesting <= 0) "<nesting level reached>"
|
||||
else {
|
||||
val showSelf = if (cl.selfType.isInstanceOf[EmptyType]) "" else " self: " + showNestedType(cl.selfType) + " =>"
|
||||
|
||||
cl.structure.parents.map(showNestedType).mkString("", " with ", " {") + showSelf +
|
||||
lines(truncateDecls(cl.structure.inherited).map(d => "^inherited^ " + showNestedDefinition(d))) +
|
||||
lines(truncateDecls(cl.structure.declared).map(showNestedDefinition)) +
|
||||
"}"
|
||||
}
|
||||
|
||||
def concat[A](list: Seq[A], as: Show[A], sep: String): String = mapSeq(list, as).mkString(sep)
|
||||
def commas[A](list: Seq[A], as: Show[A]): String = concat(list, as, ", ")
|
||||
def spaced[A](list: Seq[A], as: Show[A]): String = concat(list, as, " ")
|
||||
def lines[A](list: Seq[A], as: Show[A]): String = mapSeq(list, as).mkString("\n")
|
||||
def mapSeq[A](list: Seq[A], as: Show[A]): Seq[String] = list.map(as.show)
|
||||
def showType(t: Type)(implicit nesting: Int): String = t match {
|
||||
case st: Projection => showType(st.prefix) + "#" + st.id
|
||||
case st: ParameterRef => "<" + st.id + ">"
|
||||
case st: Singleton => showPath(st.path)
|
||||
case st: EmptyType => "<empty>"
|
||||
case p: Parameterized => showType(p.baseType) + p.typeArguments.map(showType).mkString("[", ", ", "]")
|
||||
case c: Constant => showType(c.baseType) + "(" + c.value + ")"
|
||||
case a: Annotated => showAnnotations(a.annotations) + " " + showType(a.baseType)
|
||||
case s: Structure =>
|
||||
s.parents.map(showType).mkString(" with ") + (
|
||||
if (nesting <= 0) "{ <nesting level reached> }"
|
||||
else truncateDecls(s.declared).map(showNestedDefinition).mkString(" {", "\n", "}"))
|
||||
case e: Existential =>
|
||||
showType(e.baseType) + (
|
||||
if (nesting <= 0) " forSome { <nesting level reached> }"
|
||||
else e.clause.map(t => "type " + showNestedTypeParameter(t)).mkString(" forSome { ", "; ", " }"))
|
||||
case p: Polymorphic => showType(p.baseType) + (
|
||||
if (nesting <= 0) " [ <nesting level reached> ]"
|
||||
else showNestedTypeParameters(p.parameters))
|
||||
}
|
||||
|
||||
private def showPath(p: Path): String = p.components.map(showPathComponent).mkString(".")
|
||||
private def showPathComponent(pc: PathComponent) = pc match {
|
||||
case s: Super => "super[" + showPath(s.qualifier) + "]"
|
||||
case _: This => "this"
|
||||
case i: Id => i.id
|
||||
}
|
||||
|
||||
private def space(s: String) = if (s.isEmpty) s else s + " "
|
||||
private def showMonoDef(d: Definition, label: String)(implicit nesting: Int): String =
|
||||
space(showAnnotations(d.annotations)) + space(showAccess(d.access)) + space(showModifiers(d.modifiers)) + space(label) + d.name
|
||||
|
||||
private def showPolyDef(d: ParameterizedDefinition, label: String)(implicit nesting: Int): String =
|
||||
showMonoDef(d, label) + showTypeParameters(d.typeParameters)
|
||||
|
||||
private def showTypeParameters(tps: Seq[TypeParameter])(implicit nesting: Int): String =
|
||||
if (tps.isEmpty) ""
|
||||
else tps.map(showTypeParameter).mkString("[", ", ", "]")
|
||||
|
||||
private def showTypeParameter(tp: TypeParameter)(implicit nesting: Int): String =
|
||||
showAnnotations(tp.annotations) + " " + showVariance(tp.variance) + tp.id + showTypeParameters(tp.typeParameters) + " " + showBounds(tp.lowerBound, tp.upperBound)
|
||||
|
||||
private def showAnnotations(as: Seq[Annotation])(implicit nesting: Int) = as.map(showAnnotation).mkString(" ")
|
||||
private def showAnnotation(a: Annotation)(implicit nesting: Int) =
|
||||
"@" + showType(a.base) + (
|
||||
if (a.arguments.isEmpty) ""
|
||||
else a.arguments.map(a => a.name + " = " + a.value).mkString("(", ", ", ")")
|
||||
)
|
||||
|
||||
private def showBounds(lower: Type, upper: Type)(implicit nesting: Int): String = ">: " + showType(lower) + " <: " + showType(upper)
|
||||
|
||||
private def showValueParams(ps: Seq[ParameterList])(implicit nesting: Int): String =
|
||||
ps.map(pl =>
|
||||
pl.parameters.map(mp =>
|
||||
mp.name + ": " + showParameterModifier(showType(mp.tpe), mp.modifier) + (if (mp.hasDefault) "= ..." else "")
|
||||
).mkString(if (pl.isImplicit) "(implicit " else "(", ", ", ")")
|
||||
).mkString("")
|
||||
|
||||
private def showParameterModifier(base: String, pm: ParameterModifier): String = pm match {
|
||||
case ParameterModifier.Plain => base
|
||||
case ParameterModifier.Repeated => base + "*"
|
||||
case ParameterModifier.ByName => "=> " + base
|
||||
}
|
||||
|
||||
private def showDefinitionType(d: DefinitionType) = d match {
|
||||
case DefinitionType.Trait => "trait"
|
||||
case DefinitionType.ClassDef => "class"
|
||||
case DefinitionType.Module => "object"
|
||||
case DefinitionType.PackageModule => "package object"
|
||||
}
|
||||
|
||||
private def showAccess(a: Access) = a match {
|
||||
case p: Public => ""
|
||||
case p: Protected => "protected" + showQualifier(p.qualifier)
|
||||
case p: Private => "private" + showQualifier(p.qualifier)
|
||||
}
|
||||
|
||||
private def showQualifier(q: Qualifier) = q match {
|
||||
case _: Unqualified => ""
|
||||
case _: ThisQualifier => "[this]"
|
||||
case i: IdQualifier => "[" + i.value + "]"
|
||||
}
|
||||
|
||||
private def showModifiers(m: Modifiers) = List(
|
||||
(m.isOverride, "override"),
|
||||
(m.isFinal, "final"),
|
||||
(m.isSealed, "sealed"),
|
||||
(m.isImplicit, "implicit"),
|
||||
(m.isAbstract, "abstract"),
|
||||
(m.isLazy, "lazy")
|
||||
).collect { case (true, mod) => mod }.mkString(" ")
|
||||
|
||||
private def showVariance(v: Variance) = v match {
|
||||
case Variance.Invariant => ""
|
||||
case Variance.Covariant => "+"
|
||||
case Variance.Contravariant => "-"
|
||||
}
|
||||
|
||||
// limit nesting to prevent cycles and generally keep output from getting humongous
|
||||
private def showNestedType(tp: Type)(implicit nesting: Int) = showType(tp)(nesting - 1)
|
||||
private def showNestedTypeParameter(tp: TypeParameter)(implicit nesting: Int) = showTypeParameter(tp)(nesting - 1)
|
||||
private def showNestedTypeParameters(tps: Seq[TypeParameter])(implicit nesting: Int) = showTypeParameters(tps)(nesting - 1)
|
||||
private def showNestedDefinition(d: Definition)(implicit nesting: Int) = showDefinition(d)(nesting - 1)
|
||||
}
|
||||
|
||||
trait ShowBase {
|
||||
implicit def showAnnotation(implicit as: Show[AnnotationArgument], t: Show[Type]): Show[Annotation] =
|
||||
new Show[Annotation] { def show(a: Annotation) = "@" + t.show(a.base) + (if (a.arguments.isEmpty) "" else "(" + commas(a.arguments, as) + ")") }
|
||||
|
||||
implicit def showAnnotationArgument: Show[AnnotationArgument] =
|
||||
new Show[AnnotationArgument] { def show(a: AnnotationArgument) = a.name + " = " + a.value }
|
||||
|
||||
import Variance._
|
||||
implicit def showVariance: Show[Variance] =
|
||||
new Show[Variance] { def show(v: Variance) = v match { case Invariant => ""; case Covariant => "+"; case Contravariant => "-" } }
|
||||
|
||||
implicit def showSource(implicit ps: Show[Package], ds: Show[Definition]): Show[SourceAPI] =
|
||||
new Show[SourceAPI] { def show(a: SourceAPI) = lines(a.packages, ps) + "\n" + lines(a.definitions, ds) }
|
||||
|
||||
implicit def showPackage: Show[Package] =
|
||||
new Show[Package] { def show(pkg: Package) = "package " + pkg.name }
|
||||
|
||||
implicit def showAccess(implicit sq: Show[Qualified]): Show[Access] =
|
||||
new Show[Access] {
|
||||
def show(a: Access) =
|
||||
a match {
|
||||
case p: Public => ""
|
||||
case q: Qualified => sq.show(q)
|
||||
}
|
||||
}
|
||||
implicit def showQualified(implicit sq: Show[Qualifier]): Show[Qualified] =
|
||||
new Show[Qualified] {
|
||||
def show(q: Qualified) =
|
||||
((q match {
|
||||
case p: Protected => "protected"
|
||||
case p: Private => "private"
|
||||
})
|
||||
+ sq.show(q.qualifier))
|
||||
}
|
||||
implicit def showQualifier: Show[Qualifier] =
|
||||
new Show[Qualifier] {
|
||||
def show(q: Qualifier) =
|
||||
q match {
|
||||
case _: Unqualified => ""
|
||||
case _: ThisQualifier => "[this]"
|
||||
case i: IdQualifier => "[" + i.value + "]"
|
||||
}
|
||||
}
|
||||
implicit def showModifiers: Show[Modifiers] =
|
||||
new Show[Modifiers] {
|
||||
def show(m: Modifiers) =
|
||||
{
|
||||
val mods =
|
||||
(m.isOverride, "override") ::
|
||||
(m.isFinal, "final") ::
|
||||
(m.isSealed, "sealed") ::
|
||||
(m.isImplicit, "implicit") ::
|
||||
(m.isAbstract, "abstract") ::
|
||||
(m.isLazy, "lazy") ::
|
||||
Nil
|
||||
mods.filter(_._1).map(_._2).mkString(" ")
|
||||
}
|
||||
}
|
||||
|
||||
implicit def showDefinitionType: Show[DefinitionType] =
|
||||
new Show[DefinitionType] {
|
||||
import DefinitionType._
|
||||
def show(dt: DefinitionType) =
|
||||
dt match {
|
||||
case Trait => "trait"
|
||||
case ClassDef => "class"
|
||||
case Module => "object"
|
||||
case PackageModule => "package object"
|
||||
}
|
||||
}
|
||||
}
|
||||
trait ShowDefinitions {
|
||||
implicit def showVal(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], t: Show[Type]): Show[Val] =
|
||||
new Show[Val] { def show(v: Val) = definitionBase(v, "val")(acs, ms, ans) + ": " + t.show(v.tpe) }
|
||||
|
||||
implicit def showVar(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], t: Show[Type]): Show[Var] =
|
||||
new Show[Var] { def show(v: Var) = definitionBase(v, "var")(acs, ms, ans) + ": " + t.show(v.tpe) }
|
||||
|
||||
implicit def showDef(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], tp: Show[Seq[TypeParameter]], vp: Show[Seq[ParameterList]], t: Show[Type]): Show[Def] =
|
||||
new Show[Def] { def show(d: Def) = parameterizedDef(d, "def")(acs, ms, ans, tp) + vp.show(d.valueParameters) + ": " + t.show(d.returnType) }
|
||||
|
||||
implicit def showClassLike(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], tp: Show[Seq[TypeParameter]], dt: Show[DefinitionType], s: Show[Structure], t: Show[Type]): Show[ClassLike] =
|
||||
new Show[ClassLike] { def show(cl: ClassLike) = parameterizedDef(cl, dt.show(cl.definitionType))(acs, ms, ans, tp) + " requires " + t.show(cl.selfType) + " extends " + s.show(cl.structure) }
|
||||
|
||||
implicit def showTypeAlias(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], tp: Show[Seq[TypeParameter]], t: Show[Type]): Show[TypeAlias] =
|
||||
new Show[TypeAlias] { def show(ta: TypeAlias) = parameterizedDef(ta, "type")(acs, ms, ans, tp) + " = " + t.show(ta.tpe) }
|
||||
|
||||
implicit def showTypeDeclaration(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], tp: Show[Seq[TypeParameter]], t: Show[Type]): Show[TypeDeclaration] =
|
||||
new Show[TypeDeclaration] { def show(td: TypeDeclaration) = parameterizedDef(td, "type")(acs, ms, ans, tp) + bounds(td.lowerBound, td.upperBound) }
|
||||
def showClassLikeSimple(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], tp: Show[Seq[TypeParameter]], dt: Show[DefinitionType]): Show[ClassLike] =
|
||||
new Show[ClassLike] { def show(cl: ClassLike) = parameterizedDef(cl, dt.show(cl.definitionType))(acs, ms, ans, tp) }
|
||||
|
||||
def parameterizedDef(d: ParameterizedDefinition, label: String)(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation], tp: Show[Seq[TypeParameter]]): String =
|
||||
definitionBase(d, label)(acs, ms, ans) + tp.show(d.typeParameters)
|
||||
def definitionBase(d: Definition, label: String)(implicit acs: Show[Access], ms: Show[Modifiers], ans: Show[Annotation]): String =
|
||||
space(spaced(d.annotations, ans)) + space(acs.show(d.access)) + space(ms.show(d.modifiers)) + space(label) + d.name
|
||||
def space(s: String) = if (s.isEmpty) s else s + " "
|
||||
}
|
||||
trait ShowDefinition {
|
||||
implicit def showDefinition(implicit vl: Show[Val], vr: Show[Var], ds: Show[Def], cl: Show[ClassLike], ta: Show[TypeAlias], td: Show[TypeDeclaration]): Show[Definition] =
|
||||
new Show[Definition] {
|
||||
def show(d: Definition) =
|
||||
d match {
|
||||
case v: Val => vl.show(v)
|
||||
case v: Var => vr.show(v)
|
||||
case d: Def => ds.show(d)
|
||||
case c: ClassLike => cl.show(c)
|
||||
case t: TypeAlias => ta.show(t)
|
||||
case t: TypeDeclaration => td.show(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
trait ShowType {
|
||||
implicit def showType(implicit s: Show[SimpleType], a: Show[Annotated], st: Show[Structure], c: Show[Constant], e: Show[Existential], po: Show[Polymorphic]): Show[Type] =
|
||||
new Show[Type] {
|
||||
def show(t: Type) =
|
||||
t match {
|
||||
case q: SimpleType => s.show(q)
|
||||
case q: Constant => c.show(q)
|
||||
case q: Annotated => a.show(q)
|
||||
case q: Structure => st.show(q)
|
||||
case q: Existential => e.show(q)
|
||||
case q: Polymorphic => po.show(q)
|
||||
}
|
||||
}
|
||||
|
||||
implicit def showSimpleType(implicit pr: Show[Projection], pa: Show[ParameterRef], si: Show[Singleton], et: Show[EmptyType], p: Show[Parameterized]): Show[SimpleType] =
|
||||
new Show[SimpleType] {
|
||||
def show(t: SimpleType) =
|
||||
t match {
|
||||
case q: Projection => pr.show(q)
|
||||
case q: ParameterRef => pa.show(q)
|
||||
case q: Singleton => si.show(q)
|
||||
case q: EmptyType => et.show(q)
|
||||
case q: Parameterized => p.show(q)
|
||||
}
|
||||
}
|
||||
}
|
||||
trait ShowBasicTypes {
|
||||
implicit def showSingleton(implicit p: Show[Path]): Show[Singleton] =
|
||||
new Show[Singleton] { def show(s: Singleton) = p.show(s.path) }
|
||||
implicit def showEmptyType: Show[EmptyType] =
|
||||
new Show[EmptyType] { def show(e: EmptyType) = "<empty>" }
|
||||
implicit def showParameterRef: Show[ParameterRef] =
|
||||
new Show[ParameterRef] { def show(p: ParameterRef) = "<" + p.id + ">" }
|
||||
}
|
||||
trait ShowTypes {
|
||||
implicit def showStructure(implicit t: Show[Type], d: Show[Definition]): Show[Structure] =
|
||||
new Show[Structure] {
|
||||
def show(s: Structure) = {
|
||||
// don't show inherited class like definitions to avoid dealing with cycles
|
||||
val safeInherited = s.inherited.filterNot(_.isInstanceOf[ClassLike])
|
||||
val showInherited: Show[Definition] = new Show[Definition] {
|
||||
def show(deff: Definition): String = "^inherited^ " + d.show(deff)
|
||||
}
|
||||
concat(s.parents, t, " with ") + "\n{\n" + lines(safeInherited, showInherited) + "\n" + lines(s.declared, d) + "\n}"
|
||||
}
|
||||
}
|
||||
implicit def showAnnotated(implicit as: Show[Annotation], t: Show[Type]): Show[Annotated] =
|
||||
new Show[Annotated] { def show(a: Annotated) = spaced(a.annotations, as) + " " + t.show(a.baseType) }
|
||||
implicit def showProjection(implicit t: Show[SimpleType]): Show[Projection] =
|
||||
new Show[Projection] { def show(p: Projection) = t.show(p.prefix) + "#" + p.id }
|
||||
implicit def showParameterized(implicit t: Show[Type]): Show[Parameterized] =
|
||||
new Show[Parameterized] { def show(p: Parameterized) = t.show(p.baseType) + mapSeq(p.typeArguments, t).mkString("[", ", ", "]") }
|
||||
implicit def showConstant(implicit t: Show[Type]): Show[Constant] =
|
||||
new Show[Constant] { def show(c: Constant) = t.show(c.baseType) + "(" + c.value + ")" }
|
||||
implicit def showExistential(implicit t: Show[Type], tp: Show[TypeParameter]): Show[Existential] =
|
||||
new Show[Existential] {
|
||||
def show(e: Existential) =
|
||||
t.show(e.baseType) + e.clause.map(t => "type " + tp.show(t)).mkString(" forSome { ", "; ", "}")
|
||||
}
|
||||
implicit def showPolymorphic(implicit t: Show[Type], tps: Show[Seq[TypeParameter]]): Show[Polymorphic] =
|
||||
new Show[Polymorphic] { def show(p: Polymorphic) = t.show(p.baseType) + tps.show(p.parameters) }
|
||||
|
||||
}
|
||||
|
||||
trait ShowPath {
|
||||
implicit def showPath(implicit pc: Show[PathComponent]): Show[Path] =
|
||||
new Show[Path] { def show(p: Path) = mapSeq(p.components, pc).mkString(".") }
|
||||
|
||||
implicit def showPathComponent(implicit sp: Show[Path]): Show[PathComponent] =
|
||||
new Show[PathComponent] {
|
||||
def show(p: PathComponent) =
|
||||
p match {
|
||||
case s: Super => "super[" + sp.show(s.qualifier) + "]"
|
||||
case _: This => "this"
|
||||
case i: Id => i.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait ShowValueParameters {
|
||||
implicit def showParameterLists(implicit pl: Show[ParameterList]): Show[Seq[ParameterList]] =
|
||||
new Show[Seq[ParameterList]] { def show(p: Seq[ParameterList]) = concat(p, pl, "") }
|
||||
implicit def showParameterList(implicit mp: Show[MethodParameter]): Show[ParameterList] =
|
||||
new Show[ParameterList] { def show(pl: ParameterList) = "(" + (if (pl.isImplicit) "implicit " else "") + commas(pl.parameters, mp) + ")" }
|
||||
|
||||
implicit def showMethodParameter(implicit t: Show[Type]): Show[MethodParameter] =
|
||||
new Show[MethodParameter] {
|
||||
def show(mp: MethodParameter) =
|
||||
mp.name + ": " + parameterModifier(t.show(mp.tpe), mp.modifier) + (if (mp.hasDefault) "= ..." else "")
|
||||
}
|
||||
}
|
||||
trait ShowTypeParameters {
|
||||
implicit def showTypeParameters(implicit as: Show[TypeParameter]): Show[Seq[TypeParameter]] =
|
||||
new Show[Seq[TypeParameter]] { def show(tps: Seq[TypeParameter]) = if (tps.isEmpty) "" else mapSeq(tps, as).mkString("[", ",", "]") }
|
||||
implicit def showTypeParameter(implicit as: Show[Annotation], tp: Show[Seq[TypeParameter]], t: Show[Type], v: Show[Variance]): Show[TypeParameter] =
|
||||
new Show[TypeParameter] {
|
||||
def show(tps: TypeParameter) =
|
||||
spaced(tps.annotations, as) + " " + v.show(tps.variance) + tps.id + tp.show(tps.typeParameters) + " " + bounds(tps.lowerBound, tps.upperBound)
|
||||
}
|
||||
}
|
||||
|
||||
// this class is a hack to resolve some diverging implicit errors.
|
||||
// I'm pretty sure the cause is the Show[Seq[T]] dominating Show[X] issue.
|
||||
// It could probably be reduced a bit if that is the case (below was trial and error)
|
||||
object DefaultShowAPI extends ShowBase with ShowBasicTypes with ShowValueParameters {
|
||||
def apply(d: Definition) = ShowAPI.show(d)
|
||||
def apply(d: Type) = ShowAPI.show(d)
|
||||
|
||||
implicit lazy val showVal: Show[Val] = Cyclic.showVal
|
||||
implicit lazy val showVar: Show[Var] = Cyclic.showVar
|
||||
implicit lazy val showClassLike: Show[ClassLike] = Cyclic.showClassLike
|
||||
implicit lazy val showTypeDeclaration: Show[TypeDeclaration] = Cyclic.showTypeDeclaration
|
||||
implicit lazy val showTypeAlias: Show[TypeAlias] = Cyclic.showTypeAlias
|
||||
implicit lazy val showDef: Show[Def] = Cyclic.showDef
|
||||
|
||||
implicit lazy val showProj: Show[Projection] = Cyclic.showProjection
|
||||
implicit lazy val showPoly: Show[Polymorphic] = Cyclic.showPolymorphic
|
||||
|
||||
implicit lazy val showSimple: Show[SimpleType] = new ShowLazy(Cyclic.showSimpleType)
|
||||
implicit lazy val showAnnotated: Show[Annotated] = Cyclic.showAnnotated
|
||||
implicit lazy val showExistential: Show[Existential] = Cyclic.showExistential
|
||||
implicit lazy val showConstant: Show[Constant] = Cyclic.showConstant
|
||||
implicit lazy val showParameterized: Show[Parameterized] = Cyclic.showParameterized
|
||||
|
||||
implicit lazy val showTypeParameters: Show[Seq[TypeParameter]] = new ShowLazy(Cyclic.showTypeParameters)
|
||||
implicit lazy val showTypeParameter: Show[TypeParameter] = Cyclic.showTypeParameter
|
||||
|
||||
implicit lazy val showDefinition: Show[Definition] = new ShowLazy(Cyclic.showDefinition)
|
||||
implicit lazy val showType: Show[Type] = new ShowLazy(Cyclic.showType)
|
||||
implicit lazy val showStructure: Show[Structure] = new ShowLazy(Cyclic.showStructure)
|
||||
|
||||
implicit lazy val showPath: Show[Path] = new ShowLazy(Cyclic.showPath)
|
||||
implicit lazy val showPathComponent: Show[PathComponent] = Cyclic.showPathComponent
|
||||
|
||||
private object Cyclic extends ShowTypes with ShowType with ShowPath with ShowDefinition with ShowDefinitions with ShowTypeParameters
|
||||
}
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
package sbt.inc
|
||||
|
||||
import xsbti.api.SourceAPI
|
||||
import xsbt.api.ShowAPI
|
||||
import xsbt.api.DefaultShowAPI._
|
||||
import xsbt.api.DefaultShowAPI
|
||||
import java.lang.reflect.Method
|
||||
import java.util.{ List => JList }
|
||||
|
||||
|
|
@ -39,8 +38,8 @@ private[inc] class APIDiff {
|
|||
* Generates an unified diff between textual representations of `api1` and `api2`.
|
||||
*/
|
||||
def generateApiDiff(fileName: String, api1: SourceAPI, api2: SourceAPI, contextSize: Int): String = {
|
||||
val api1Str = ShowAPI.show(api1)
|
||||
val api2Str = ShowAPI.show(api2)
|
||||
val api1Str = DefaultShowAPI(api1)
|
||||
val api2Str = DefaultShowAPI(api2)
|
||||
generateApiDiff(fileName, api1Str, api2Str, contextSize)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,12 @@ import java.io.File
|
|||
* - IncrementalAnyStyle
|
||||
*/
|
||||
object Incremental {
|
||||
class PrefixingLogger(val prefix: String)(orig: Logger) extends Logger {
|
||||
def trace(t: => Throwable): Unit = orig.trace(t)
|
||||
def success(message: => String): Unit = orig.success(message)
|
||||
def log(level: sbt.Level.Value, message: => String): Unit = orig.log(level, message.replaceAll("(?m)^", prefix))
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the incremental compiler algorithm.
|
||||
*
|
||||
|
|
@ -45,7 +51,7 @@ object Incremental {
|
|||
{
|
||||
val incremental: IncrementalCommon =
|
||||
if (options.nameHashing)
|
||||
new IncrementalNameHashing(log, options)
|
||||
new IncrementalNameHashing(new PrefixingLogger("[naha] ")(log), options)
|
||||
else if (options.antStyle)
|
||||
new IncrementalAntStyle(log, options)
|
||||
else
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ private[inc] abstract class IncrementalCommon(log: Logger, options: IncOptions)
|
|||
if (invalidatedRaw.isEmpty)
|
||||
previous
|
||||
else {
|
||||
def debug(s: => String) = if (incDebug(options)) log.debug(s) else ()
|
||||
val wrappedLog = new Incremental.PrefixingLogger("[inv] ")(log)
|
||||
def debug(s: => String) = if (incDebug(options)) wrappedLog.debug(s) else ()
|
||||
val withPackageObjects = invalidatedRaw ++ invalidatedPackageObjects(invalidatedRaw, previous.relations)
|
||||
val invalidated = expand(withPackageObjects, allSources)
|
||||
val pruned = Incremental.prune(invalidated, previous, classfileManager)
|
||||
|
|
@ -64,23 +65,23 @@ private[inc] abstract class IncrementalCommon(log: Logger, options: IncOptions)
|
|||
newAPIMapping: T => Source): Unit = {
|
||||
val contextSize = options.apiDiffContextSize
|
||||
try {
|
||||
val wrappedLog = new Incremental.PrefixingLogger("[diff] ")(log)
|
||||
val apiDiff = new APIDiff
|
||||
apiChanges foreach {
|
||||
case APIChangeDueToMacroDefinition(src) =>
|
||||
log.debug(s"Public API is considered to be changed because $src contains a macro definition.")
|
||||
wrappedLog.debug(s"Public API is considered to be changed because $src contains a macro definition.")
|
||||
case apiChange @ (_: SourceAPIChange[T] | _: NamesChange[T]) =>
|
||||
val src = apiChange.modified
|
||||
val oldApi = oldAPIMapping(src)
|
||||
val newApi = newAPIMapping(src)
|
||||
val apiUnifiedPatch = apiDiff.generateApiDiff(src.toString, oldApi.api, newApi.api, contextSize)
|
||||
log.debug(s"Detected a change in a public API (${src.toString}):\n"
|
||||
+ apiUnifiedPatch)
|
||||
wrappedLog.debug(s"Detected a change in a public API ($src):\n$apiUnifiedPatch")
|
||||
}
|
||||
} catch {
|
||||
case e: ClassNotFoundException =>
|
||||
log.error("You have api debugging enabled but DiffUtils library cannot be found on sbt's classpath")
|
||||
case e: LinkageError =>
|
||||
log.error("Encoutared linkage error while trying to load DiffUtils library.")
|
||||
log.error("Encountered linkage error while trying to load DiffUtils library.")
|
||||
log.trace(e)
|
||||
case e: Exception =>
|
||||
log.error("An exception has been thrown while trying to dump an api diff.")
|
||||
|
|
|
|||
|
|
@ -36,28 +36,26 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
|
|||
private class DependencyPhase(prev: Phase) extends Phase(prev) {
|
||||
override def description = "Extracts dependency information"
|
||||
def name = Dependency.name
|
||||
def run {
|
||||
def run: Unit = {
|
||||
for (unit <- currentRun.units if !unit.isJava) {
|
||||
// build dependencies structure
|
||||
val sourceFile = unit.source.file.file
|
||||
if (global.callback.nameHashing) {
|
||||
val dependenciesByMemberRef = extractDependenciesByMemberRef(unit)
|
||||
for (on <- dependenciesByMemberRef)
|
||||
processDependency(on, context = DependencyByMemberRef)
|
||||
val dependencyExtractor = new ExtractDependenciesTraverser
|
||||
dependencyExtractor.traverse(unit.body)
|
||||
|
||||
val dependenciesByInheritance = extractDependenciesByInheritance(unit)
|
||||
for (on <- dependenciesByInheritance)
|
||||
processDependency(on, context = DependencyByInheritance)
|
||||
dependencyExtractor.topLevelDependencies foreach processDependency(context = DependencyByMemberRef)
|
||||
dependencyExtractor.topLevelInheritanceDependencies foreach processDependency(context = DependencyByInheritance)
|
||||
} else {
|
||||
for (on <- unit.depends) processDependency(on, context = DependencyByMemberRef)
|
||||
for (on <- inheritedDependencies.getOrElse(sourceFile, Nil: Iterable[Symbol])) processDependency(on, context = DependencyByInheritance)
|
||||
unit.depends foreach processDependency(context = DependencyByMemberRef)
|
||||
inheritedDependencies.getOrElse(sourceFile, Nil: Iterable[Symbol]) foreach processDependency(context = DependencyByInheritance)
|
||||
}
|
||||
/**
|
||||
* Handles dependency on given symbol by trying to figure out if represents a term
|
||||
* that is coming from either source code (not necessarily compiled in this compilation
|
||||
* run) or from class file and calls respective callback method.
|
||||
*/
|
||||
def processDependency(on: Symbol, context: DependencyContext): Unit = {
|
||||
def processDependency(context: DependencyContext)(on: Symbol) = {
|
||||
def binaryDependency(file: File, className: String) = callback.binaryDependency(file, className, sourceFile, context)
|
||||
val onSource = on.sourceFile
|
||||
if (onSource == null) {
|
||||
|
|
@ -78,6 +76,91 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
|
|||
}
|
||||
}
|
||||
|
||||
private class ExtractDependenciesTraverser extends Traverser {
|
||||
private val _dependencies = collection.mutable.HashSet.empty[Symbol]
|
||||
protected def addDependency(dep: Symbol): Unit = if (dep ne NoSymbol) _dependencies += dep
|
||||
def dependencies: Iterator[Symbol] = _dependencies.iterator
|
||||
def topLevelDependencies: Iterator[Symbol] = _dependencies.map(enclosingTopLevelClass).iterator
|
||||
|
||||
private val _inheritanceDependencies = collection.mutable.HashSet.empty[Symbol]
|
||||
protected def addInheritanceDependency(dep: Symbol): Unit = if (dep ne NoSymbol) _inheritanceDependencies += dep
|
||||
def inheritanceDependencies: Iterator[Symbol] = _inheritanceDependencies.iterator
|
||||
def topLevelInheritanceDependencies: Iterator[Symbol] = _inheritanceDependencies.map(enclosingTopLevelClass).iterator
|
||||
|
||||
/*
|
||||
* Some macros appear to contain themselves as original tree.
|
||||
* We must check that we don't inspect the same tree over and over.
|
||||
* See https://issues.scala-lang.org/browse/SI-8486
|
||||
* https://github.com/sbt/sbt/issues/1237
|
||||
* https://github.com/sbt/sbt/issues/1544
|
||||
*/
|
||||
private val inspectedOriginalTrees = collection.mutable.Set.empty[Tree]
|
||||
|
||||
override def traverse(tree: Tree): Unit = tree match {
|
||||
case Import(expr, selectors) =>
|
||||
traverse(expr)
|
||||
selectors.foreach {
|
||||
case ImportSelector(nme.WILDCARD, _, null, _) =>
|
||||
// in case of wildcard import we do not rely on any particular name being defined
|
||||
// on `expr`; all symbols that are being used will get caught through selections
|
||||
case ImportSelector(name: Name, _, _, _) =>
|
||||
def lookupImported(name: Name) = expr.symbol.info.member(name)
|
||||
// importing a name means importing both a term and a type (if they exist)
|
||||
addDependency(lookupImported(name.toTermName))
|
||||
addDependency(lookupImported(name.toTypeName))
|
||||
}
|
||||
|
||||
/*
|
||||
* Idents are used in number of situations:
|
||||
* - to refer to local variable
|
||||
* - to refer to a top-level package (other packages are nested selections)
|
||||
* - to refer to a term defined in the same package as an enclosing class;
|
||||
* this looks fishy, see this thread:
|
||||
* https://groups.google.com/d/topic/scala-internals/Ms9WUAtokLo/discussion
|
||||
*/
|
||||
case id: Ident => addDependency(id.symbol)
|
||||
case sel @ Select(qual, _) =>
|
||||
traverse(qual); addDependency(sel.symbol)
|
||||
case sel @ SelectFromTypeTree(qual, _) =>
|
||||
traverse(qual); addDependency(sel.symbol)
|
||||
|
||||
case Template(parents, self, body) =>
|
||||
// use typeSymbol to dealias type aliases -- we want to track the dependency on the real class in the alias's RHS
|
||||
def flattenTypeToSymbols(tp: Type): List[Symbol] = if (tp eq null) Nil else tp match {
|
||||
// rt.typeSymbol is redundant if we list out all parents, TODO: what about rt.decls?
|
||||
case rt: RefinedType => rt.parents.flatMap(flattenTypeToSymbols)
|
||||
case _ => List(tp.typeSymbol)
|
||||
}
|
||||
|
||||
val inheritanceTypes = parents.map(_.tpe).toSet
|
||||
val inheritanceSymbols = inheritanceTypes.flatMap(flattenTypeToSymbols)
|
||||
|
||||
debuglog("Parent types for " + tree.symbol + " (self: " + self.tpt.tpe + "): " + inheritanceTypes + " with symbols " + inheritanceSymbols.map(_.fullName))
|
||||
|
||||
inheritanceSymbols.foreach(addInheritanceDependency)
|
||||
|
||||
val allSymbols = (inheritanceTypes + self.tpt.tpe).flatMap(symbolsInType)
|
||||
(allSymbols ++ inheritanceSymbols).foreach(addDependency)
|
||||
traverseTrees(body)
|
||||
|
||||
// In some cases (eg. macro annotations), `typeTree.tpe` may be null. See sbt/sbt#1593 and sbt/sbt#1655.
|
||||
case typeTree: TypeTree if typeTree.tpe != null => symbolsInType(typeTree.tpe) foreach addDependency
|
||||
|
||||
case MacroExpansionOf(original) if inspectedOriginalTrees.add(original) => traverse(original)
|
||||
case other => super.traverse(other)
|
||||
}
|
||||
|
||||
private def symbolsInType(tp: Type): Set[Symbol] = {
|
||||
val typeSymbolCollector =
|
||||
new CollectTypeTraverser({
|
||||
case tpe if (tpe != null) && !tpe.typeSymbolDirect.isPackage => tpe.typeSymbolDirect
|
||||
})
|
||||
|
||||
typeSymbolCollector.traverse(tp)
|
||||
typeSymbolCollector.collected.toSet
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses given type and collects result of applying a partial function `pf`.
|
||||
*
|
||||
|
|
@ -94,109 +177,15 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile {
|
|||
}
|
||||
}
|
||||
|
||||
private abstract class ExtractDependenciesTraverser extends Traverser {
|
||||
protected val depBuf = collection.mutable.ArrayBuffer.empty[Symbol]
|
||||
protected def addDependency(dep: Symbol): Unit = depBuf += dep
|
||||
def dependencies: collection.immutable.Set[Symbol] = {
|
||||
// convert to immutable set and remove NoSymbol if we have one
|
||||
depBuf.toSet - NoSymbol
|
||||
}
|
||||
}
|
||||
|
||||
private class ExtractDependenciesByMemberRefTraverser extends ExtractDependenciesTraverser {
|
||||
|
||||
/*
|
||||
* Some macros appear to contain themselves as original tree.
|
||||
* We must check that we don't inspect the same tree over and over.
|
||||
* See https://issues.scala-lang.org/browse/SI-8486
|
||||
* https://github.com/sbt/sbt/issues/1237
|
||||
* https://github.com/sbt/sbt/issues/1544
|
||||
*/
|
||||
private val inspectedOriginalTrees = collection.mutable.Set.empty[Tree]
|
||||
|
||||
override def traverse(tree: Tree): Unit = {
|
||||
tree match {
|
||||
case Import(expr, selectors) =>
|
||||
selectors.foreach {
|
||||
case ImportSelector(nme.WILDCARD, _, null, _) =>
|
||||
// in case of wildcard import we do not rely on any particular name being defined
|
||||
// on `expr`; all symbols that are being used will get caught through selections
|
||||
case ImportSelector(name: Name, _, _, _) =>
|
||||
def lookupImported(name: Name) = expr.symbol.info.member(name)
|
||||
// importing a name means importing both a term and a type (if they exist)
|
||||
addDependency(lookupImported(name.toTermName))
|
||||
addDependency(lookupImported(name.toTypeName))
|
||||
}
|
||||
case select: Select =>
|
||||
addDependency(select.symbol)
|
||||
/*
|
||||
* Idents are used in number of situations:
|
||||
* - to refer to local variable
|
||||
* - to refer to a top-level package (other packages are nested selections)
|
||||
* - to refer to a term defined in the same package as an enclosing class;
|
||||
* this looks fishy, see this thread:
|
||||
* https://groups.google.com/d/topic/scala-internals/Ms9WUAtokLo/discussion
|
||||
*/
|
||||
case ident: Ident =>
|
||||
addDependency(ident.symbol)
|
||||
// In some cases (eg. macro annotations), `typeTree.tpe` may be null.
|
||||
// See sbt/sbt#1593 and sbt/sbt#1655.
|
||||
case typeTree: TypeTree if typeTree.tpe != null =>
|
||||
val typeSymbolCollector = new CollectTypeTraverser({
|
||||
case tpe if !tpe.typeSymbol.isPackage => tpe.typeSymbol
|
||||
})
|
||||
typeSymbolCollector.traverse(typeTree.tpe)
|
||||
val deps = typeSymbolCollector.collected.toSet
|
||||
deps.foreach(addDependency)
|
||||
case Template(parents, self, body) =>
|
||||
traverseTrees(body)
|
||||
case MacroExpansionOf(original) if inspectedOriginalTrees.add(original) =>
|
||||
this.traverse(original)
|
||||
case other => ()
|
||||
}
|
||||
super.traverse(tree)
|
||||
}
|
||||
}
|
||||
|
||||
private def extractDependenciesByMemberRef(unit: CompilationUnit): collection.immutable.Set[Symbol] = {
|
||||
val traverser = new ExtractDependenciesByMemberRefTraverser
|
||||
traverser.traverse(unit.body)
|
||||
val dependencies = traverser.dependencies
|
||||
dependencies.map(enclosingTopLevelClass)
|
||||
}
|
||||
|
||||
/** Copied straight from Scala 2.10 as it does not exist in Scala 2.9 compiler */
|
||||
private final def debuglog(msg: => String): Unit = {
|
||||
if (settings.debug.value)
|
||||
log(msg)
|
||||
}
|
||||
|
||||
private final class ExtractDependenciesByInheritanceTraverser extends ExtractDependenciesTraverser {
|
||||
override def traverse(tree: Tree): Unit = tree match {
|
||||
case Template(parents, self, body) =>
|
||||
// we are using typeSymbol and not typeSymbolDirect because we want
|
||||
// type aliases to be expanded
|
||||
val parentTypeSymbols = parents.map(parent => parent.tpe.typeSymbol).toSet
|
||||
debuglog("Parent type symbols for " + tree.pos + ": " + parentTypeSymbols.map(_.fullName))
|
||||
parentTypeSymbols.foreach(addDependency)
|
||||
traverseTrees(body)
|
||||
case tree => super.traverse(tree)
|
||||
}
|
||||
}
|
||||
|
||||
private def extractDependenciesByInheritance(unit: CompilationUnit): collection.immutable.Set[Symbol] = {
|
||||
val traverser = new ExtractDependenciesByInheritanceTraverser
|
||||
traverser.traverse(unit.body)
|
||||
val dependencies = traverser.dependencies
|
||||
dependencies.map(enclosingTopLevelClass)
|
||||
}
|
||||
private final def debuglog(msg: => String): Unit = if (settings.debug.value) log(msg)
|
||||
|
||||
/**
|
||||
* We capture enclosing classes only because that's what CompilationUnit.depends does and we don't want
|
||||
* to deviate from old behaviour too much for now.
|
||||
*
|
||||
* NOTE: for Scala 2.8 and 2.9 this method is provided through SymbolCompat
|
||||
*/
|
||||
private def enclosingTopLevelClass(sym: Symbol): Symbol =
|
||||
// for Scala 2.8 and 2.9 this method is provided through SymbolCompat
|
||||
sym.enclosingTopLevelClass
|
||||
private def enclosingTopLevelClass(sym: Symbol): Symbol = sym.enclosingTopLevelClass
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -174,13 +174,27 @@ class ExtractAPI[GlobalType <: CallbackGlobal](val global: GlobalType,
|
|||
}
|
||||
private def reference(sym: Symbol): xsbti.api.ParameterRef = new xsbti.api.ParameterRef(tparamID(sym))
|
||||
|
||||
private def annotations(in: Symbol, as: List[AnnotationInfo]): Array[xsbti.api.Annotation] = as.toArray[AnnotationInfo].map(annotation(in, _))
|
||||
private def annotation(in: Symbol, a: AnnotationInfo) =
|
||||
new xsbti.api.Annotation(processType(in, a.atp),
|
||||
if (a.assocs.isEmpty) Array(new xsbti.api.AnnotationArgument("", a.args.mkString("(", ",", ")"))) // what else to do with a Tree?
|
||||
else a.assocs.map { case (name, value) => new xsbti.api.AnnotationArgument(name.toString, value.toString) }.toArray[xsbti.api.AnnotationArgument]
|
||||
)
|
||||
private def annotated(in: Symbol, as: List[AnnotationInfo], tpe: Type) = new xsbti.api.Annotated(processType(in, tpe), annotations(in, as))
|
||||
// The compiler only pickles static annotations, so only include these in the API.
|
||||
// This way, the API is not sensitive to whether we compiled from source or loaded from classfile.
|
||||
// (When looking at the sources we see all annotations, but when loading from classes we only see the pickled (static) ones.)
|
||||
private def mkAnnotations(in: Symbol, as: List[AnnotationInfo]): Array[xsbti.api.Annotation] =
|
||||
staticAnnotations(as).toArray.map { a =>
|
||||
new xsbti.api.Annotation(processType(in, a.atp),
|
||||
if (a.assocs.isEmpty) Array(new xsbti.api.AnnotationArgument("", a.args.mkString("(", ",", ")"))) // what else to do with a Tree?
|
||||
else a.assocs.map { case (name, value) => new xsbti.api.AnnotationArgument(name.toString, value.toString) }.toArray[xsbti.api.AnnotationArgument]
|
||||
)
|
||||
}
|
||||
|
||||
private def annotations(in: Symbol, s: Symbol): Array[xsbti.api.Annotation] =
|
||||
atPhase(currentRun.typerPhase) {
|
||||
val base = if (s.hasFlag(Flags.ACCESSOR)) s.accessed else NoSymbol
|
||||
val b = if (base == NoSymbol) s else base
|
||||
// annotations from bean methods are not handled because:
|
||||
// a) they are recorded as normal source methods anyway
|
||||
// b) there is no way to distinguish them from user-defined methods
|
||||
val associated = List(b, b.getter(b.enclClass), b.setter(b.enclClass)).filter(_ != NoSymbol)
|
||||
associated.flatMap(ss => mkAnnotations(in, ss.annotations)).distinct.toArray
|
||||
}
|
||||
|
||||
private def viewer(s: Symbol) = (if (s.isModule) s.moduleClass else s).thisType
|
||||
private def printMember(label: String, in: Symbol, t: Type) = println(label + " in " + in + " : " + t + " (debug: " + debugString(t) + " )")
|
||||
|
|
@ -329,21 +343,50 @@ class ExtractAPI[GlobalType <: CallbackGlobal](val global: GlobalType,
|
|||
error("Unknown type member" + s)
|
||||
}
|
||||
|
||||
private def structure(in: Symbol, s: Symbol): xsbti.api.Structure = structure(viewer(in).memberInfo(s), s, true)
|
||||
private def structure(info: Type): xsbti.api.Structure = structure(info, info.typeSymbol, false)
|
||||
private def structure(info: Type, s: Symbol, inherit: Boolean): xsbti.api.Structure =
|
||||
structureCache.getOrElseUpdate(s, mkStructure(info, s, inherit))
|
||||
private def structure(info: Type, s: Symbol): xsbti.api.Structure = structureCache.getOrElseUpdate(s, mkStructure(info, s))
|
||||
private def structureWithInherited(info: Type, s: Symbol): xsbti.api.Structure = structureCache.getOrElseUpdate(s, mkStructureWithInherited(info, s))
|
||||
|
||||
private def removeConstructors(ds: List[Symbol]): List[Symbol] = ds filter { !_.isConstructor }
|
||||
|
||||
private def mkStructure(info: Type, s: Symbol, inherit: Boolean): xsbti.api.Structure =
|
||||
{
|
||||
val (declared, inherited) = info.members.toList.reverse.partition(_.owner == s)
|
||||
val baseTypes = info.baseClasses.tail.map(info.baseType)
|
||||
val ds = if (s.isModuleClass) removeConstructors(declared) else declared
|
||||
val is = if (inherit) removeConstructors(inherited) else Nil
|
||||
mkStructure(s, baseTypes, ds, is)
|
||||
}
|
||||
/**
|
||||
* Create structure as-is, without embedding ancestors
|
||||
*
|
||||
* (for refinement types, and ClassInfoTypes encountered outside of a definition???).
|
||||
*/
|
||||
private def mkStructure(info: Type, s: Symbol): xsbti.api.Structure = {
|
||||
// We're not interested in the full linearization, so we can just use `parents`,
|
||||
// which side steps issues with baseType when f-bounded existential types and refined types mix
|
||||
// (and we get cyclic types which cause a stack overflow in showAPI).
|
||||
//
|
||||
// The old algorithm's semantics for inherited dependencies include all types occurring as a parent anywhere in a type,
|
||||
// so that, in `class C { def foo: A }; class A extends B`, C is considered to have an "inherited dependency" on `A` and `B`!!!
|
||||
val parentTypes = if (global.callback.nameHashing()) info.parents else linearizedAncestorTypes(info)
|
||||
val decls = info.decls.toList
|
||||
val declsNoModuleCtor = if (s.isModuleClass) removeConstructors(decls) else decls
|
||||
mkStructure(s, parentTypes, declsNoModuleCtor, Nil)
|
||||
}
|
||||
|
||||
/**
|
||||
* Track all ancestors and inherited members for a class's API.
|
||||
*
|
||||
* A class's hash does not include hashes for its parent classes -- only the symbolic names --
|
||||
* so we must ensure changes propagate somehow.
|
||||
*
|
||||
* TODO: can we include hashes for parent classes instead? This seems a bit messy.
|
||||
*/
|
||||
private def mkStructureWithInherited(info: Type, s: Symbol): xsbti.api.Structure = {
|
||||
val ancestorTypes = linearizedAncestorTypes(info)
|
||||
val decls = info.decls.toList
|
||||
val declsNoModuleCtor = if (s.isModuleClass) removeConstructors(decls) else decls
|
||||
val declSet = decls.toSet
|
||||
val inherited = info.nonPrivateMembers.toList.filterNot(declSet) // private members are not inherited
|
||||
mkStructure(s, ancestorTypes, declsNoModuleCtor, inherited)
|
||||
}
|
||||
|
||||
// Note that the ordering of classes in `baseClasses` is important.
|
||||
// It would be easier to just say `baseTypeSeq.toList.tail`,
|
||||
// but that does not take linearization into account.
|
||||
def linearizedAncestorTypes(info: Type): List[Type] = info.baseClasses.tail.map(info.baseType)
|
||||
|
||||
// If true, this template is publicly visible and should be processed as a public inheritance dependency.
|
||||
// Local classes and local refinements will never be traversed by the api phase, so we don't need to check for that.
|
||||
|
|
@ -468,7 +511,7 @@ class ExtractAPI[GlobalType <: CallbackGlobal](val global: GlobalType,
|
|||
if (unrolling ne withoutRecursiveRefs)
|
||||
reporter.warning(sym.pos, "sbt-api: approximated refinement ref" + t + " (== " + unrolling + ") to " + withoutRecursiveRefs + "\nThis is currently untested, please report the code you were compiling.")
|
||||
|
||||
structure(withoutRecursiveRefs)
|
||||
structure(withoutRecursiveRefs, sym)
|
||||
case tr @ TypeRef(pre, sym, args) =>
|
||||
val base = projectionType(in, pre, sym)
|
||||
if (args.isEmpty)
|
||||
|
|
@ -480,8 +523,12 @@ class ExtractAPI[GlobalType <: CallbackGlobal](val global: GlobalType,
|
|||
new xsbti.api.Parameterized(base, types(in, args))
|
||||
case SuperType(thistpe: Type, supertpe: Type) =>
|
||||
warning("sbt-api: Super type (not implemented): this=" + thistpe + ", super=" + supertpe); Constants.emptyType
|
||||
case at: AnnotatedType => annotatedType(in, at)
|
||||
case rt: CompoundType => structure(rt)
|
||||
case at: AnnotatedType =>
|
||||
at.annotations match {
|
||||
case Nil => processType(in, at.underlying)
|
||||
case annots => new xsbti.api.Annotated(processType(in, at.underlying), mkAnnotations(in, annots))
|
||||
}
|
||||
case rt: CompoundType => structure(rt, rt.typeSymbol)
|
||||
case t: ExistentialType => makeExistentialType(in, t)
|
||||
case NoType => Constants.emptyType // this can happen when there is an error that will be reported by a later phase
|
||||
case PolyType(typeParams, resultType) => new xsbti.api.Polymorphic(processType(in, resultType), typeParameters(in, typeParams))
|
||||
|
|
@ -515,9 +562,8 @@ class ExtractAPI[GlobalType <: CallbackGlobal](val global: GlobalType,
|
|||
case x => error("Unknown type parameter info: " + x.getClass)
|
||||
}
|
||||
}
|
||||
private def tparamID(s: Symbol): String = {
|
||||
val renameTo = existentialRenamings.renaming(s)
|
||||
renameTo match {
|
||||
private def tparamID(s: Symbol): String =
|
||||
existentialRenamings.renaming(s) match {
|
||||
case Some(rename) =>
|
||||
// can't use debuglog because it doesn't exist in Scala 2.9.x
|
||||
if (settings.debug.value)
|
||||
|
|
@ -526,24 +572,38 @@ class ExtractAPI[GlobalType <: CallbackGlobal](val global: GlobalType,
|
|||
case None =>
|
||||
s.fullName
|
||||
}
|
||||
}
|
||||
private def selfType(in: Symbol, s: Symbol): xsbti.api.Type = processType(in, s.thisSym.typeOfThis)
|
||||
|
||||
|
||||
/* Representation for the self type of a class symbol `s`, or `emptyType` for an *unascribed* self variable (or no self variable at all).
|
||||
Only the self variable's explicitly ascribed type is relevant for incremental compilation. */
|
||||
private def selfType(in: Symbol, s: Symbol): xsbti.api.Type =
|
||||
// `sym.typeOfThis` is implemented as `sym.thisSym.info`, which ensures the *self* symbol is initialized (the type completer is run).
|
||||
// We can safely avoid running the type completer for `thisSym` for *class* symbols where `thisSym == this`,
|
||||
// as that invariant is established on completing the class symbol (`mkClassLike` calls `s.initialize` before calling us).
|
||||
// Technically, we could even ignore a self type that's a supertype of the class's type,
|
||||
// as it does not contribute any information relevant outside of the class definition.
|
||||
if ((s.thisSym eq s) || s.typeOfThis == s.info) Constants.emptyType else processType(in, s.typeOfThis)
|
||||
|
||||
def classLike(in: Symbol, c: Symbol): ClassLike = classLikeCache.getOrElseUpdate((in, c), mkClassLike(in, c))
|
||||
private def mkClassLike(in: Symbol, c: Symbol): ClassLike =
|
||||
{
|
||||
val name = c.fullName
|
||||
val isModule = c.isModuleClass || c.isModule
|
||||
val struct = if (isModule) c.moduleClass else c
|
||||
val defType =
|
||||
if (c.isTrait) DefinitionType.Trait
|
||||
else if (isModule) {
|
||||
if (c.isPackage) DefinitionType.PackageModule
|
||||
else DefinitionType.Module
|
||||
} else DefinitionType.ClassDef
|
||||
new xsbti.api.ClassLike(defType, lzy(selfType(in, c)), lzy(structure(in, struct)), emptyStringArray, typeParameters(in, c), name, getAccess(c), getModifiers(c), annotations(in, c))
|
||||
}
|
||||
private def mkClassLike(in: Symbol, c: Symbol): ClassLike = {
|
||||
// Normalize to a class symbol, and initialize it.
|
||||
// (An object -- aka module -- also has a term symbol,
|
||||
// but it's the module class that holds the info about its structure.)
|
||||
val sym = (if (c.isModule) c.moduleClass else c).initialize
|
||||
val defType =
|
||||
if (sym.isTrait) DefinitionType.Trait
|
||||
else if (sym.isModuleClass) {
|
||||
if (sym.isPackageClass) DefinitionType.PackageModule
|
||||
else DefinitionType.Module
|
||||
} else DefinitionType.ClassDef
|
||||
|
||||
new xsbti.api.ClassLike(
|
||||
defType, lzy(selfType(in, sym)), lzy(structureWithInherited(viewer(in).memberInfo(sym), sym)), emptyStringArray, typeParameters(in, sym), // look at class symbol
|
||||
c.fullName, getAccess(c), getModifiers(c), annotations(in, c)) // use original symbol (which is a term symbol when `c.isModule`) for `name` and other non-classy stuff
|
||||
}
|
||||
|
||||
// TODO: could we restrict ourselves to classes, ignoring the term symbol for modules,
|
||||
// since everything we need to track about a module is in the module's class (`moduleSym.moduleClass`)?
|
||||
private[this] def isClass(s: Symbol) = s.isClass || s.isModule
|
||||
// necessary to ensure a stable ordering of classes in the definitions list:
|
||||
// modules and classes come first and are sorted by name
|
||||
|
|
@ -586,20 +646,10 @@ class ExtractAPI[GlobalType <: CallbackGlobal](val global: GlobalType,
|
|||
n2.toString.trim
|
||||
}
|
||||
|
||||
private def annotations(in: Symbol, s: Symbol): Array[xsbti.api.Annotation] =
|
||||
atPhase(currentRun.typerPhase) {
|
||||
val base = if (s.hasFlag(Flags.ACCESSOR)) s.accessed else NoSymbol
|
||||
val b = if (base == NoSymbol) s else base
|
||||
// annotations from bean methods are not handled because:
|
||||
// a) they are recorded as normal source methods anyway
|
||||
// b) there is no way to distinguish them from user-defined methods
|
||||
val associated = List(b, b.getter(b.enclClass), b.setter(b.enclClass)).filter(_ != NoSymbol)
|
||||
associated.flatMap(ss => annotations(in, ss.annotations)).distinct.toArray;
|
||||
}
|
||||
private def annotatedType(in: Symbol, at: AnnotatedType): xsbti.api.Type =
|
||||
{
|
||||
val annots = at.annotations
|
||||
if (annots.isEmpty) processType(in, at.underlying) else annotated(in, annots, at.underlying)
|
||||
}
|
||||
|
||||
private def staticAnnotations(annotations: List[AnnotationInfo]): List[AnnotationInfo] = {
|
||||
// compat stub for 2.8/2.9
|
||||
class IsStatic(ann: AnnotationInfo) { def isStatic: Boolean = ann.atp.typeSymbol isNonBottomSubClass definitions.StaticAnnotationClass }
|
||||
implicit def compat(ann: AnnotationInfo): IsStatic = new IsStatic(ann)
|
||||
annotations.filter(_.isStatic)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class DependencySpecification extends Specification {
|
|||
inheritance('D) === Set.empty
|
||||
memberRef('E) === Set.empty
|
||||
inheritance('E) === Set.empty
|
||||
memberRef('F) === Set('A, 'B, 'C, 'D, 'E)
|
||||
memberRef('F) === Set('A, 'B, 'C, 'D, 'E, 'G)
|
||||
inheritance('F) === Set('A, 'E)
|
||||
memberRef('H) === Set('B, 'E, 'G)
|
||||
// aliases and applied type constructors are expanded so we have inheritance dependency on B
|
||||
|
|
@ -86,8 +86,8 @@ class DependencySpecification extends Specification {
|
|||
|}""".stripMargin
|
||||
val srcD = "class D[T]"
|
||||
val srcE = "trait E[T]"
|
||||
val srcF = "trait F extends A with E[D[B]] { self: C => }"
|
||||
val srcG = "object G { type T[x] = B }"
|
||||
val srcF = "trait F extends A with E[D[B]] { self: G.MyC => }"
|
||||
val srcG = "object G { type T[x] = B ; type MyC = C }"
|
||||
// T is a type constructor [x]B
|
||||
// B extends D
|
||||
// E verifies the core type gets pulled out
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
[@fkorotkov]: http://github.com/fkorotkov
|
||||
[@hgiddens]: https://github.com/hgiddens
|
||||
[@DavidPerezIngeniero]: https://github.com/DavidPerezIngeniero
|
||||
[@romanowski]: https://github.com/romanowski
|
||||
[2302]: https://github.com/sbt/sbt/issues/2302
|
||||
[2303]: https://github.com/sbt/sbt/pull/2303
|
||||
[1967]: https://github.com/sbt/sbt/issues/1967
|
||||
|
|
@ -69,6 +70,7 @@
|
|||
[1514]: https://github.com/sbt/sbt/issues/1514
|
||||
[1616]: https://github.com/sbt/sbt/issues/1616
|
||||
[2313]: https://github.com/sbt/sbt/pull/2313
|
||||
[2343]: https://github.com/sbt/sbt/pull/2343
|
||||
|
||||
### Fixes with compatibility implications
|
||||
|
||||
|
|
@ -94,6 +96,8 @@
|
|||
- Fixes warnings, and other clean ups. [#2112][2112]/[#2137][2137]/[#2139][2139]/[#2142][2142] by [@pdalpra][@pdalpra]
|
||||
- Adds `localIfFile` to `MavenRepository`, to force artifacts to be copied to the cache. [#2172][2172] by [@dwijnand][@dwijnand]
|
||||
- Adds `Resolver.bintrayIvyRepo(owner, repo)`. [#2285][2285] by [@dwijnand][@dwijnand]
|
||||
- Non-static annotation changes are no longer tracked by the incremental compiler. [#2343][2343] by [@romanowski][@romanowski]
|
||||
- Reduces the memory usage of API info extraction in the incremental compiler. [#2343][2343] by [@adriaanm][@adriaanm]
|
||||
|
||||
### Bug fixes
|
||||
|
||||
|
|
@ -122,6 +126,8 @@
|
|||
- Fixes `JavaErrorParser` to parse non-compile-errors [#2256][2256]/[#2272][2272] by [@Duhemm][@Duhemm]
|
||||
- Fixes task scheduling performance on large builds by skipping checks in `sbt.Execute`. [#2302][2302]/[#2303][2303] by [@jrudolph][@jrudolph]
|
||||
- Fixes launcher configuration to add `sbt-ivy-snapshots` repository to resolve nightly builds. [@eed3si9n][@eed3si9n]
|
||||
- Fixes performance issues during tree traversal in the incremental compiler. [#2343][2343] by [@adriaanm][@adriaanm]
|
||||
- Fixes the tracking of self types and F-bounded existential types in the incremental compiler. [#2343][2343] by [@adriaanm][@adriaanm]
|
||||
|
||||
### Def.settings
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ TaskKey[Unit]("show-apis") <<= (compile in Compile, scalaSource in Compile, java
|
|||
val aApi = a.apis.internalAPI(scalaSrc / "A.scala").api
|
||||
val jApi = a.apis.internalAPI(javaSrc / "test/J.java").api
|
||||
import xsbt.api.DefaultShowAPI
|
||||
import DefaultShowAPI._
|
||||
DefaultShowAPI.showSource.show(aApi)
|
||||
DefaultShowAPI.showSource.show(jApi)
|
||||
DefaultShowAPI(aApi)
|
||||
DefaultShowAPI(jApi)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
class Dep {
|
||||
// The API representation for `bla`'s result type contains a cycle
|
||||
// (an existential's type variable's bound is the existential type itself)
|
||||
// This results in a stack overflow while showing the API diff.
|
||||
// Note that the actual result type in the compiler is not cyclic
|
||||
// (the f-bounded existential for Comparable is truncated)
|
||||
def bla(c: Boolean) = if (c) new Value else "bla"
|
||||
}
|
||||
|
||||
class Value extends java.lang.Comparable[Value] { def compareTo(that: Value): Int = 1 }
|
||||
|
|
@ -0,0 +1 @@
|
|||
> compile
|
||||
Loading…
Reference in New Issue