diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala | 10 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/Delambdafy.scala | 115 | ||||
-rw-r--r-- | src/reflect/scala/reflect/api/Quasiquotes.scala | 2 | ||||
-rw-r--r-- | src/repl/scala/tools/nsc/interpreter/JavapClass.scala | 34 | ||||
-rw-r--r-- | src/scaladoc/scala/tools/nsc/doc/Index.scala | 2 | ||||
-rw-r--r-- | src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala | 2 | ||||
-rwxr-xr-x | src/scaladoc/scala/tools/nsc/doc/html/page/DeprecatedIndex.scala | 58 | ||||
-rw-r--r-- | src/scaladoc/scala/tools/nsc/doc/html/page/Index.scala | 7 | ||||
-rwxr-xr-x | src/scaladoc/scala/tools/nsc/doc/model/IndexModelFactory.scala | 8 | ||||
-rw-r--r-- | test/files/run/repl-javap-lambdas.scala | 2 | ||||
-rw-r--r-- | test/files/run/t9268.check | 5 | ||||
-rw-r--r-- | test/files/run/t9268/Java.java | 12 | ||||
-rw-r--r-- | test/files/run/t9268/Test.scala | 40 | ||||
-rw-r--r-- | test/scaladoc/resources/SI-4476.scala | 9 | ||||
-rw-r--r-- | test/scaladoc/scalacheck/DeprecatedIndexTest.scala | 50 | ||||
-rw-r--r-- | test/scaladoc/scalacheck/IndexTest.scala | 8 |
16 files changed, 250 insertions, 114 deletions
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 994bcd8359..a59b9d3f48 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -1141,16 +1141,12 @@ abstract class ClassfileParser { private def innerSymbol(entry: InnerClassEntry): Symbol = { val name = entry.originalName.toTypeName val enclosing = entry.enclosing - def getMember = ( + val member = ( if (enclosing == clazz) entry.scope lookup name else lookupMemberAtTyperPhaseIfPossible(enclosing, name) ) - getMember - /* There used to be an assertion that this result is not NoSymbol; changing it to an error - * revealed it had been going off all the time, but has been swallowed by a catch t: Throwable - * in Repository.scala. Since it has been accomplishing nothing except misleading anyone who - * thought it wasn't triggering, I removed it entirely. - */ + def newStub = enclosing.newStubSymbol(name, s"Unable to locate class corresponding to inner class entry for $name in owner ${entry.outerName}") + member.orElse(newStub) } } diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala index 94e88589f5..2d33b35241 100644 --- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala +++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala @@ -15,13 +15,12 @@ import scala.collection.mutable.LinkedHashMap * Currently Uncurry is responsible for that transformation. * * From a lambda, Delambdafy will create - * 1) a static forwarder at the top level of the class that contained the lambda - * 2) a new top level class that + * 1) a new top level class that a) has fields and a constructor taking the captured environment (including possibly the "this" * reference) - * b) an apply method that calls the static forwarder + * b) an apply method that calls the target method * c) if needed a bridge method for the apply method - * 3) an instantiation of the newly created class which replaces the lambda + * 2) an instantiation of the newly created class which replaces the lambda * * TODO the main work left to be done is to plug into specialization. Primarily that means choosing a * specialized FunctionN trait instead of the generic FunctionN trait as a parent and creating the @@ -76,36 +75,25 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre referrers } - val accessorMethods = mutable.ArrayBuffer[Tree]() - - // the result of the transformFunction method. A class definition for the lambda, an expression - // insantiating the lambda class, and an accessor method for the lambda class to be able to - // call the implementation - case class TransformedFunction(lambdaClassDef: ClassDef, newExpr: Tree, accessorMethod: Tree) + // the result of the transformFunction method. + sealed abstract class TransformedFunction + // A class definition for the lambda, an expression insantiating the lambda class + case class DelambdafyAnonClass(lambdaClassDef: ClassDef, newExpr: Tree) extends TransformedFunction // here's the main entry point of the transform override def transform(tree: Tree): Tree = tree match { // the main thing we care about is lambdas case fun @ Function(_, _) => - // a lambda beccomes a new class, an instantiation expression, and an - // accessor method - val TransformedFunction(lambdaClassDef, newExpr, accessorMethod) = transformFunction(fun) - // we'll add accessor methods to the current template later - accessorMethods += accessorMethod - val pkg = lambdaClassDef.symbol.owner - - // we'll add the lambda class to the package later - lambdaClassDefs(pkg) = lambdaClassDef :: lambdaClassDefs(pkg) - - super.transform(newExpr) - // when we encounter a template (basically the thing that holds body of a class/trait) - // we need to updated it to include newly created accessor methods after transforming it - case Template(_, _, _) => - try { - // during this call accessorMethods will be populated from the Function case - val Template(parents, self, body) = super.transform(tree) - Template(parents, self, body ++ accessorMethods) - } finally accessorMethods.clear() + transformFunction(fun) match { + case DelambdafyAnonClass(lambdaClassDef, newExpr) => + // a lambda beccomes a new class, an instantiation expression + val pkg = lambdaClassDef.symbol.owner + + // we'll add the lambda class to the package later + lambdaClassDefs(pkg) = lambdaClassDef :: lambdaClassDefs(pkg) + + super.transform(newExpr) + } case _ => super.transform(tree) } @@ -120,8 +108,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre private def optionSymbol(sym: Symbol): Option[Symbol] = if (sym.exists) Some(sym) else None - // turns a lambda into a new class def, a New expression instantiating that class, and an - // accessor method fo the body of the lambda + // turns a lambda into a new class def, a New expression instantiating that class private def transformFunction(originalFunction: Function): TransformedFunction = { val functionTpe = originalFunction.tpe val targs = functionTpe.typeArgs @@ -132,46 +119,16 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre // passed into the constructor of the anonymous function class val captures = FreeVarTraverser.freeVarsOf(originalFunction) - /** - * Creates the apply method for the anonymous subclass of FunctionN - */ - def createAccessorMethod(thisProxy: Symbol, fun: Function): DefDef = { - val target = targetMethod(fun) - if (!thisProxy.exists) { - target setFlag STATIC - } - val params = ((optionSymbol(thisProxy) map {proxy:Symbol => ValDef(proxy)}) ++ (target.paramss.flatten map ValDef.apply)).toList - - val methSym = oldClass.newMethod(unit.freshTermName(nme.accessor.toString() + "$"), target.pos, FINAL | BRIDGE | SYNTHETIC | PROTECTED | STATIC) + val target = targetMethod(originalFunction) + target.makeNotPrivate(target.owner) + if (!thisReferringMethods.contains(target)) + target setFlag STATIC - val paramSyms = params map {param => methSym.newSyntheticValueParam(param.symbol.tpe, param.name) } - - params zip paramSyms foreach { case (valdef, sym) => valdef.symbol = sym } - params foreach (_.symbol.owner = methSym) - - val methodType = MethodType(paramSyms, restpe) - methSym setInfo methodType - - oldClass.info.decls enter methSym - - val body = localTyper.typed { - val newTarget = Select(if (thisProxy.exists) gen.mkAttributedRef(paramSyms(0)) else gen.mkAttributedThis(oldClass), target) - val newParams = paramSyms drop (if (thisProxy.exists) 1 else 0) map Ident - Apply(newTarget, newParams) - } setPos fun.pos - val methDef = DefDef(methSym, List(params), body) - - // Have to repack the type to avoid mismatches when existentials - // appear in the result - see SI-4869. - // TODO probably don't need packedType - methDef.tpt setType localTyper.packedType(body, methSym) - methDef - } /** * Creates the apply method for the anonymous subclass of FunctionN */ - def createApplyMethod(newClass: Symbol, fun: Function, accessor: DefDef, thisProxy: Symbol): DefDef = { + def createApplyMethod(newClass: Symbol, fun: Function, thisProxy: Symbol): DefDef = { val methSym = newClass.newMethod(nme.apply, fun.pos, FINAL | SYNTHETIC) val params = fun.vparams map (_.duplicate) @@ -187,8 +144,12 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre newClass.info.decls enter methSym val Apply(_, oldParams) = fun.body + val qual = if (thisProxy.exists) + Select(gen.mkAttributedThis(newClass), thisProxy) + else + gen.mkAttributedThis(oldClass) // sort of a lie, EmptyTree.<static method> would be more honest, but the backend chokes on that. - val body = localTyper typed Apply(Select(gen.mkAttributedThis(oldClass), accessor.symbol), (optionSymbol(thisProxy) map {tp => Select(gen.mkAttributedThis(newClass), tp)}).toList ++ oldParams) + val body = localTyper typed Apply(Select(qual, target), oldParams) body.substituteSymbols(fun.vparams map (_.symbol), params map (_.symbol)) body changeOwner (fun.symbol -> methSym) @@ -271,18 +232,16 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre // the Optional proxy that will hold a reference to the 'this' // object used by the lambda, if any. NoSymbol if there is no this proxy val thisProxy = { - val target = targetMethod(originalFunction) - if (thisReferringMethods contains target) { + if (target.hasFlag(STATIC)) + NoSymbol + else { val sym = lambdaClass.newVariable(nme.FAKE_LOCAL_THIS, originalFunction.pos, SYNTHETIC) - sym.info = oldClass.tpe - sym - } else NoSymbol + sym.setInfo(oldClass.tpe) + } } val decapturify = new DeCapturifyTransformer(captureProxies2, unit, oldClass, lambdaClass, originalFunction.symbol.pos, thisProxy) - val accessorMethod = createAccessorMethod(thisProxy, originalFunction) - val decapturedFunction = decapturify.transform(originalFunction).asInstanceOf[Function] val members = (optionSymbol(thisProxy).toList ++ (captureProxies2 map (_._2))) map {member => @@ -294,7 +253,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre val constr = createConstructor(lambdaClass, members) // apply method with same arguments and return type as original lambda. - val applyMethodDef = createApplyMethod(lambdaClass, decapturedFunction, accessorMethod, thisProxy) + val applyMethodDef = createApplyMethod(lambdaClass, decapturedFunction, thisProxy) val bridgeMethod = createBridgeMethod(lambdaClass, originalFunction, applyMethodDef) @@ -312,10 +271,10 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre val body = members ++ List(constr, applyMethodDef) ++ bridgeMethod // TODO if member fields are private this complains that they're not accessible - (localTyper.typedPos(decapturedFunction.pos)(ClassDef(lambdaClass, body)).asInstanceOf[ClassDef], thisProxy, accessorMethod) + (localTyper.typedPos(decapturedFunction.pos)(ClassDef(lambdaClass, body)).asInstanceOf[ClassDef], thisProxy) } - val (anonymousClassDef, thisProxy, accessorMethod) = makeAnonymousClass + val (anonymousClassDef, thisProxy) = makeAnonymousClass pkg.info.decls enter anonymousClassDef.symbol @@ -327,7 +286,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre val typedNewStat = localTyper.typedPos(originalFunction.pos)(newStat) - TransformedFunction(anonymousClassDef, typedNewStat, accessorMethod) + DelambdafyAnonClass(anonymousClassDef, typedNewStat) } /** diff --git a/src/reflect/scala/reflect/api/Quasiquotes.scala b/src/reflect/scala/reflect/api/Quasiquotes.scala index eaae05bed5..554b43afaf 100644 --- a/src/reflect/scala/reflect/api/Quasiquotes.scala +++ b/src/reflect/scala/reflect/api/Quasiquotes.scala @@ -3,7 +3,7 @@ package api trait Quasiquotes { self: Universe => - /** Implicit class that introduces `q`, `tq`, `cq,` `p` and `fq` string interpolators + /** Implicit class that introduces `q`, `tq`, `cq,` `pq` and `fq` string interpolators * that are also known as quasiquotes. With their help you can easily manipulate * Scala reflection ASTs. * diff --git a/src/repl/scala/tools/nsc/interpreter/JavapClass.scala b/src/repl/scala/tools/nsc/interpreter/JavapClass.scala index c80b94bf89..1ccade2172 100644 --- a/src/repl/scala/tools/nsc/interpreter/JavapClass.scala +++ b/src/repl/scala/tools/nsc/interpreter/JavapClass.scala @@ -8,6 +8,7 @@ package tools.nsc package interpreter import java.lang.{ ClassLoader => JavaClassLoader, Iterable => JIterable } +import scala.tools.asm.Opcodes import scala.tools.nsc.util.ScalaClassLoader import java.io.{ ByteArrayInputStream, CharArrayWriter, FileNotFoundException, PrintWriter, StringWriter, Writer } import java.util.{ Locale } @@ -758,32 +759,19 @@ object JavapClass { import scala.tools.asm.ClassReader import scala.tools.asm.Opcodes.INVOKESTATIC import scala.tools.asm.tree.{ ClassNode, MethodInsnNode } - // the accessor methods invoked statically by the apply of the given closure class - def accesses(s: String): Seq[(String, String)] = { - val accessor = """accessor\$\d+""".r + def callees(s: String): List[(String, String)] = { loader classReader s withMethods { ms => - ms filter (_.name == "apply") flatMap (_.instructions.toArray.collect { - case i: MethodInsnNode if i.getOpcode == INVOKESTATIC && when(i.name) { case accessor(_*) => true } => (i.owner, i.name) - }) + val nonBridgeApplyMethods = ms filter (_.name == "apply") filter (n => (n.access & Opcodes.ACC_BRIDGE) == 0) + val instructions = nonBridgeApplyMethods flatMap (_.instructions.toArray) + instructions.collect { + case i: MethodInsnNode => (i.owner, i.name) + }.toList } } - // get the k.$anonfun for the accessor k.m - def anonOf(k: String, m: String): String = { - val res = - loader classReader k withMethods { ms => - ms filter (_.name == m) flatMap (_.instructions.toArray.collect { - case i: MethodInsnNode if i.getOpcode == INVOKESTATIC && i.name.startsWith("$anonfun") => i.name - }) - } - assert(res.size == 1) - res.head - } - // the lambdas invoke accessors that call the anonfuns of interest. Filter k on the k#$anonfuns. - val ack = accesses(lambda) - assert(ack.size == 1) // There can be only one. - ack.head match { - case (k, _) if target.isModule && !(k endsWith "$") => None - case (k, m) => Some(s"${k}#${anonOf(k, m)}") + callees(lambda) match { + case (k, _) :: Nil if target.isModule && !(k endsWith "$") => None + case (k, m) :: _ => Some(s"${k}#${m}") + case _ => None } } /** Translate the supplied targets to patterns for anonfuns. diff --git a/src/scaladoc/scala/tools/nsc/doc/Index.scala b/src/scaladoc/scala/tools/nsc/doc/Index.scala index 84545e9201..a11ca38a86 100644 --- a/src/scaladoc/scala/tools/nsc/doc/Index.scala +++ b/src/scaladoc/scala/tools/nsc/doc/Index.scala @@ -11,4 +11,6 @@ trait Index { type SymbolMap = SortedMap[String, SortedSet[model.MemberEntity]] def firstLetterIndex: Map[Char, SymbolMap] + + def hasDeprecatedMembers: Boolean } diff --git a/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala b/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala index 61ab18d42d..8313d842e5 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala @@ -123,6 +123,8 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index) { new page.Index(universe, index) writeFor this new page.IndexScript(universe, index) writeFor this + if (index.hasDeprecatedMembers) + new page.DeprecatedIndex(universe, index) writeFor this try { writeTemplates(_ writeFor this) for (letter <- index.firstLetterIndex) { diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/DeprecatedIndex.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/DeprecatedIndex.scala new file mode 100755 index 0000000000..f257153bd7 --- /dev/null +++ b/src/scaladoc/scala/tools/nsc/doc/html/page/DeprecatedIndex.scala @@ -0,0 +1,58 @@ +/* NSC -- new Scala compiler + * Copyright 2007-2013 LAMP/EPFL + */ + +package scala +package tools +package nsc +package doc +package html +package page + +import doc.model._ + +class DeprecatedIndex(universe: Universe, index: doc.Index) extends HtmlPage { + + def path = List("deprecated-list.html") + + def title = { + val s = universe.settings + ( if (!s.doctitle.isDefault) s.doctitle.value else "" ) + + ( if (!s.docversion.isDefault) (" " + s.docversion.value) else "" ) + } + + def headers = + <xml:group> + <link href={ relativeLinkTo(List("ref-index.css", "lib")) } media="screen" type="text/css" rel="stylesheet"/> + <script type="text/javascript" src={ relativeLinkTo{List("jquery.js", "lib")} }></script> + </xml:group> + + + private def entry(name: String, methods: Iterable[MemberEntity]) = { + val occurrences = methods.filter(_.deprecation.isDefined).map(method => + templateToHtml(method.inDefinitionTemplates.head) + ).toList.distinct + + <div class="entry"> + <div class="name">{ name }</div> + <div class="occurrences">{ + for (owner <- occurrences) yield owner ++ scala.xml.Text(" ") + }</div> + </div> + } + + def deprecatedEntries = { + val available = ('_' +: ('a' to 'z')).flatMap(index.firstLetterIndex.get) + + for (group <- available; + value <- group if value._2.find(_.deprecation.isDefined).isDefined) + yield value + } + + def body = + <body>{ + for(value <- deprecatedEntries) yield + entry(value._1, value._2.view) + }</body> + +} diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/Index.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/Index.scala index ce3a5eb1fc..6bfe480e33 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/page/Index.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/page/Index.scala @@ -61,12 +61,17 @@ class Index(universe: doc.Universe, val index: doc.Index) extends HtmlPage { } } + def deprecated: NodeSeq = if (index.hasDeprecatedMembers) + <a target="template" href="deprecated-list.html">deprecated</a> + else + <span>deprecated</span> + def browser = <div id="browser" class="ui-layout-west"> <div class="ui-west-center"> <div id="filter"> <div id="textfilter"></div> - <div id="letters">{ letters }</div> + <div id="letters">{ letters } – { deprecated }</div> </div> <div class="pack" id="tpl">{ def packageElem(pack: model.Package): NodeSeq = { diff --git a/src/scaladoc/scala/tools/nsc/doc/model/IndexModelFactory.scala b/src/scaladoc/scala/tools/nsc/doc/model/IndexModelFactory.scala index 643a089aae..ad53dc6bfa 100755 --- a/src/scaladoc/scala/tools/nsc/doc/model/IndexModelFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/model/IndexModelFactory.scala @@ -14,10 +14,12 @@ object IndexModelFactory { def makeIndex(universe: Universe): Index = new Index { - lazy val firstLetterIndex: Map[Char, SymbolMap] = { + lazy val (firstLetterIndex, hasDeprecatedMembers): (Map[Char, SymbolMap], Boolean) = { object result extends mutable.HashMap[Char,SymbolMap] { + var deprecated = false + /* symbol name ordering */ implicit def orderingMap = math.Ordering.String @@ -32,6 +34,8 @@ object IndexModelFactory { val members = letter.get(d.name).getOrElse { SortedSet.empty[MemberEntity](Ordering.by { _.toString }) } + d + if (!deprecated && members.find(_.deprecation.isDefined).isDefined) + deprecated = true this(firstLetter) = letter + (d.name -> members) } } @@ -50,7 +54,7 @@ object IndexModelFactory { gather(universe.rootPackage) - result.toMap + (result.toMap, result.deprecated) } } } diff --git a/test/files/run/repl-javap-lambdas.scala b/test/files/run/repl-javap-lambdas.scala index c503c99d66..76a6ec8450 100644 --- a/test/files/run/repl-javap-lambdas.scala +++ b/test/files/run/repl-javap-lambdas.scala @@ -16,7 +16,7 @@ object Test extends JavapTest { // three anonfuns of Betty#g override def yah(res: Seq[String]) = { import PartialFunction.{ cond => when } - val r = """\s*private static final .* \$anonfun\$\d+\(.*""".r + val r = """.*final .* .*\$anonfun\$\d+\(.*""".r def filtered = res filter (when(_) { case r(_*) => true }) 3 == filtered.size } diff --git a/test/files/run/t9268.check b/test/files/run/t9268.check new file mode 100644 index 0000000000..90ef940eb3 --- /dev/null +++ b/test/files/run/t9268.check @@ -0,0 +1,5 @@ +Compiling Client1 +pos: NoPosition Class Waiter not found - continuing with a stub. WARNING +Compiling Client2 +pos: NoPosition Class Waiter not found - continuing with a stub. WARNING +pos: NoPosition Unable to locate class corresponding to inner class entry for Predicate in owner Waiter ERROR diff --git a/test/files/run/t9268/Java.java b/test/files/run/t9268/Java.java new file mode 100644 index 0000000000..c9a0bec3ff --- /dev/null +++ b/test/files/run/t9268/Java.java @@ -0,0 +1,12 @@ +public class Java { +} + +class Partial { + public <E extends java.lang.Exception> long waitFor(long l, Waiter.Predicate<E> pred) throws E { + return 0L; + } +} + +class Waiter { + interface Predicate<E> {} +} diff --git a/test/files/run/t9268/Test.scala b/test/files/run/t9268/Test.scala new file mode 100644 index 0000000000..813cbe7b60 --- /dev/null +++ b/test/files/run/t9268/Test.scala @@ -0,0 +1,40 @@ +import scala.tools.partest._ +import java.io.File + +object Test extends StoreReporterDirectTest { + def code = ??? + + def compileCode(code: String) = { + val classpath = List(sys.props("partest.lib"), testOutput.path) mkString sys.props("path.separator") + compileString(newCompiler("-cp", classpath, "-d", testOutput.path))(code) + } + + def client1 = """ + class Client1 { def p(p: Partial) = p.toString } + """ + + def client2 = """ + class Client2 { def p(p: Partial) = p.waitFor() } + """ + + def deleteClass(s: String) = { + val f = new File(testOutput.path, s + ".class") + assert(f.exists) + f.delete() + } + + def show(): Unit = { + deleteClass("Waiter") + deleteClass("Waiter$Predicate") + + // Used to crash in Java Generic Signature parsing + println("Compiling Client1") + compileCode(client1) + println(storeReporter.infos.mkString("\n")) + storeReporter.reset() + println("Compiling Client2") + compileCode(client2) + println(storeReporter.infos.mkString("\n")) + } +} + diff --git a/test/scaladoc/resources/SI-4476.scala b/test/scaladoc/resources/SI-4476.scala new file mode 100644 index 0000000000..eb35ef45e7 --- /dev/null +++ b/test/scaladoc/resources/SI-4476.scala @@ -0,0 +1,9 @@ +package foo + +@deprecated("","") +class A + +class B { + @deprecated("","") + def bar = 1 +} diff --git a/test/scaladoc/scalacheck/DeprecatedIndexTest.scala b/test/scaladoc/scalacheck/DeprecatedIndexTest.scala new file mode 100644 index 0000000000..4a5a2001d4 --- /dev/null +++ b/test/scaladoc/scalacheck/DeprecatedIndexTest.scala @@ -0,0 +1,50 @@ +import org.scalacheck._ +import org.scalacheck.Prop._ + +import scala.tools.nsc.doc +import scala.tools.nsc.doc.html.page.DeprecatedIndex +import java.net.{URLClassLoader, URLDecoder} + +object Test extends Properties("IndexScript") { + + def getClasspath = { + // these things can be tricky + // this test previously relied on the assumption that the current thread's classloader is an url classloader and contains all the classpaths + // does partest actually guarantee this? to quote Leonard Nimoy: The answer, of course, is no. + // this test _will_ fail again some time in the future. + // Footnote: java.lang.ClassCastException: org.apache.tools.ant.loader.AntClassLoader5 cannot be cast to java.net.URLClassLoader + val loader = Thread.currentThread.getContextClassLoader.asInstanceOf[URLClassLoader] + val paths = loader.getURLs.map(u => URLDecoder.decode(u.getPath)) + paths mkString java.io.File.pathSeparator + } + + val docFactory = { + val settings = new doc.Settings({Console.err.println(_)}) + settings.scaladocQuietRun = true + settings.nowarn.value = true + settings.classpath.value = getClasspath + val reporter = new scala.tools.nsc.reporters.ConsoleReporter(settings) + new doc.DocFactory(reporter, settings) + } + + val indexModelFactory = doc.model.IndexModelFactory + + def createDeprecatedScript(path: String) = + docFactory.makeUniverse(Left(List(path))) match { + case Some(universe) => { + val index = new DeprecatedIndex(universe, indexModelFactory.makeIndex(universe)) + Some(index) + } + case _ => + None + } + + property("deprecated-list page lists deprecated members") = { + createDeprecatedScript("test/scaladoc/resources/SI-4476.scala") match { + case Some(p) => + p.deprecatedEntries.find(_._1 == "A").isDefined && + p.deprecatedEntries.find(_._1 == "bar").isDefined + case None => false + } + } +} diff --git a/test/scaladoc/scalacheck/IndexTest.scala b/test/scaladoc/scalacheck/IndexTest.scala index abc0e5da01..7dbd2103a6 100644 --- a/test/scaladoc/scalacheck/IndexTest.scala +++ b/test/scaladoc/scalacheck/IndexTest.scala @@ -71,7 +71,7 @@ object Test extends Properties("Index") { case None => false } } - property("browser contants a script element") = { + property("browser contains a script element") = { createIndex("src/scaladoc/scala/tools/nsc/doc/html/page/Index.scala") match { case Some(index) => (index.browser \ "script").size == 1 @@ -86,4 +86,10 @@ object Test extends Properties("Index") { case None => false } } + property("index should report if there are deprecated members") = { + createIndex("test/scaladoc/resources/SI-4476.scala") match { + case Some(indexPage) => indexPage.index.hasDeprecatedMembers + case None => false + } + } } |