diff options
203 files changed, 4201 insertions, 1227 deletions
diff --git a/README.rst b/README.rst index 383db1c175..72c4b6028b 100644 --- a/README.rst +++ b/README.rst @@ -200,6 +200,8 @@ all code submitted to the project is unencumbered by copyrights or patents. The form is available at: http://www.scala-lang.org/sites/default/files/contributor_agreement.pdf +Before submitting a pull-request, please make sure you have followed the guidelines +outlined in our `Pull Request Policy <https://github.com/scala/scala/wiki/Pull-Request-Policy>`_. ------------------ @@ -208,5 +210,3 @@ http://www.scala-lang.org/sites/default/files/contributor_agreement.pdf Thank you! The Scala Team - - @@ -397,7 +397,7 @@ INITIALISATION </target> <target name="init" depends="init.jars, init.maven.jars, init.version.done, init.fail.bad.jdk, init.warn.jdk7"> - <property name="scalac.args.always" value="" /> + <property name="scalac.args.always" value="-Yreify-copypaste" /> <!-- scalac.args.optimise is selectively overridden in certain antcall tasks. --> <property name="scalac.args.optimise" value=""/> <!-- scalac.args.quickonly are added to quick.* targets but not others (particularly, locker.) @@ -499,7 +499,7 @@ LOCAL DEPENDENCY (Adapted ASM) destdir="${build-asm.dir}/classes" classpath="${build-asm.dir}/classes" includes="**/*.java" - target="1.5" source="1.5"> + target="1.6" source="1.5"> <compilerarg line="${javac.args} -XDignore.symbol.file"/> </javac> <touch file="${build-asm.dir}/asm.complete" verbose="no"/> @@ -540,7 +540,7 @@ LOCAL DEPENDENCY (FORKJOIN) classpath="${build-libs.dir}/classes/forkjoin" includes="**/*.java" debug="true" - target="1.5" source="1.5"> + target="1.6" source="1.5"> <compilerarg line="${javac.args} -XDignore.symbol.file"/> </javac> <touch file="${build-libs.dir}/forkjoin.complete" verbose="no"/> @@ -587,7 +587,7 @@ LOCAL DEPENDENCY (FJBG) classpath="${build-libs.dir}/classes/fjbg" includes="**/*.java" debug="true" - target="1.5" source="1.4"> + target="1.6" source="1.4"> <compilerarg line="${javac.args} -XDignore.symbol.file"/> </javac> <touch file="${build-libs.dir}/fjbg.complete" verbose="no"/> @@ -635,7 +635,7 @@ LOCAL REFERENCE BUILD (LOCKER) srcdir="${src.dir}/library" destdir="${build-locker.dir}/classes/library" includes="**/*.java" - target="1.5" source="1.5"> + target="1.6" source="1.5"> <compilerarg line="${javac.args} -XDignore.symbol.file"/> <classpath> <path refid="forkjoin.classpath"/> @@ -749,7 +749,7 @@ LOCAL REFERENCE BUILD (LOCKER) includes="**/*.java" excludes="**/tests/**" debug="true" - target="1.5" source="1.4"> + target="1.6" source="1.4"> <compilerarg line="${javac.args}"/> </javac> <scalacfork @@ -1009,7 +1009,7 @@ QUICK BUILD (QUICK) srcdir="${src.dir}/library" destdir="${build-quick.dir}/classes/library" includes="**/*.java" - target="1.5" source="1.5"> + target="1.6" source="1.5"> <compilerarg line="${javac.args} -XDignore.symbol.file"/> <classpath> <path refid="forkjoin.classpath"/> @@ -1020,7 +1020,7 @@ QUICK BUILD (QUICK) srcdir="${src.dir}/actors" destdir="${build-quick.dir}/classes/library" includes="**/*.java" - target="1.5" source="1.5"> + target="1.6" source="1.5"> <compilerarg line="${javac.args}"/> <classpath> <path refid="forkjoin.classpath"/> @@ -1145,7 +1145,7 @@ QUICK BUILD (QUICK) includes="**/*.java" excludes="**/tests/**" debug="true" - target="1.5" source="1.4"> + target="1.6" source="1.4"> <compilerarg line="${javac.args}"/> </javac> <scalacfork @@ -1349,7 +1349,7 @@ QUICK BUILD (QUICK) <javac srcdir="${src.dir}/partest" destdir="${build-quick.dir}/classes/partest" - target="1.5" source="1.5"> + target="1.6" source="1.5"> <classpath> <pathelement location="${build-quick.dir}/classes/library"/> <pathelement location="${build-quick.dir}/classes/reflect"/> @@ -1691,7 +1691,7 @@ BOOTSTRAPPING BUILD (STRAP) srcdir="${src.dir}/library" destdir="${build-strap.dir}/classes/library" includes="**/*.java" - target="1.5" source="1.5"> + target="1.6" source="1.5"> <compilerarg line="${javac.args} -XDignore.symbol.file"/> <classpath> <path refid="forkjoin.classpath"/> @@ -1702,7 +1702,7 @@ BOOTSTRAPPING BUILD (STRAP) srcdir="${src.dir}/actors" destdir="${build-strap.dir}/classes/library" includes="**/*.java" - target="1.5" source="1.5"> + target="1.6" source="1.5"> <compilerarg line="${javac.args}"/> <classpath> <path refid="forkjoin.classpath"/> @@ -1826,7 +1826,7 @@ BOOTSTRAPPING BUILD (STRAP) includes="**/*.java" excludes="**/tests/**" debug="true" - target="1.5" source="1.4"> + target="1.6" source="1.4"> <compilerarg line="${javac.args}"/> </javac> <scalacfork @@ -1994,7 +1994,7 @@ BOOTSTRAPPING BUILD (STRAP) <javac srcdir="${src.dir}/partest" destdir="${build-strap.dir}/classes/partest" - target="1.5" source="1.5"> + target="1.6" source="1.5"> <classpath> <pathelement location="${build-strap.dir}/classes/library"/> <pathelement location="${build-strap.dir}/classes/reflect"/> @@ -2044,7 +2044,7 @@ BOOTSTRAPPING BUILD (STRAP) LIBRARIES (Forkjoin, FJBG, ASM) ============================================================================ --> - + <target name="libs.clean" depends="pack.clean, asm.clean"> <delete dir="${build-libs.dir}" includeemptydirs="yes" quiet="yes" failonerror="no"/> </target> @@ -2082,8 +2082,9 @@ DOCUMENTATION <property name="scaladoc.url" value="https://github.com/scala/scala/tree/${scaladoc.git.commit}/src"/> <echo message="Scaladoc will point to ${scaladoc.url} for source files."/> - <!-- Unless set with -Dscaladoc.raw.output, it won't be activated --> + <!-- Unless set with -Dscaladoc.<...>, these won't be activated --> <property name="scaladoc.raw.output" value="no"/> + <property name="scaladoc.no.prefixes" value="no"/> </target> <target name="docs.pre-lib" depends="docs.start"> @@ -2111,7 +2112,11 @@ DOCUMENTATION classpathref="pack.classpath" addparams="${scalac.args.all}" docRootContent="${src.dir}/library/rootdoc.txt" - implicits="on" diagrams="on" rawOutput="${scaladoc.raw.output}"> + implicits="on" + diagrams="on" + groups="on" + rawOutput="${scaladoc.raw.output}" + noPrefixes="${scaladoc.no.prefixes}"> <src> <files includes="${src.dir}/actors-migration"/> <files includes="${src.dir}/actors"/> @@ -2195,7 +2200,11 @@ DOCUMENTATION srcdir="${src.dir}/compiler" docRootContent="${src.dir}/compiler/rootdoc.txt" addparams="${scalac.args.all}" - implicits="on" diagrams="on" rawOutput="${scaladoc.raw.output}"> + implicits="on" + diagrams="on" + groups="on" + rawOutput="${scaladoc.raw.output}" + noPrefixes="${scaladoc.no.prefixes}"> <include name="**/*.scala"/> </scaladoc> <touch file="${build-docs.dir}/compiler.complete" verbose="no"/> @@ -2217,7 +2226,11 @@ DOCUMENTATION classpathref="pack.classpath" srcdir="${src.dir}/jline/src/main/java" addparams="${scalac.args.all}" - implicits="on" diagrams="on" rawOutput="${scaladoc.raw.output}"> + implicits="on" + diagrams="on" + groups="on" + rawOutput="${scaladoc.raw.output}" + noPrefixes="${scaladoc.no.prefixes}"> <include name="**/*.scala"/> <include name="**/*.java"/> </scaladoc> @@ -2241,7 +2254,11 @@ DOCUMENTATION classpathref="pack.classpath" srcdir="${src.dir}/scalap" addparams="${scalac.args.all}" - implicits="on" diagrams="on" rawOutput="${scaladoc.raw.output}"> + implicits="on" + diagrams="on" + groups="on" + rawOutput="${scaladoc.raw.output}" + noPrefixes="${scaladoc.no.prefixes}"> <include name="**/*.scala"/> </scaladoc> <touch file="${build-docs.dir}/scalap.complete" verbose="no"/> @@ -2263,7 +2280,11 @@ DOCUMENTATION classpathref="pack.classpath" srcdir="${src.dir}/partest" addparams="${scalac.args.all}" - implicits="on" diagrams="on" rawOutput="${scaladoc.raw.output}"> + implicits="on" + diagrams="on" + groups="on" + rawOutput="${scaladoc.raw.output}" + noPrefixes="${scaladoc.no.prefixes}"> <include name="**/*.scala"/> </scaladoc> <touch file="${build-docs.dir}/partest.complete" verbose="no"/> @@ -2285,7 +2306,11 @@ DOCUMENTATION classpathref="pack.classpath" srcdir="${src.dir}/continuations/plugin" addparams="${scalac.args.all}" - implicits="on" diagrams="on" rawOutput="${scaladoc.raw.output}"> + implicits="on" + diagrams="on" + groups="on" + rawOutput="${scaladoc.raw.output}" + noPrefixes="${scaladoc.no.prefixes}"> <include name="**/*.scala"/> </scaladoc> <touch file="${build-docs.dir}/continuations-plugin.complete" verbose="no"/> @@ -2307,7 +2332,11 @@ DOCUMENTATION classpathref="pack.classpath" srcdir="${src.dir}/actors-migration" addparams="${scalac.args.all}" - implicits="on" diagrams="on" rawOutput="${scaladoc.raw.output}"> + implicits="on" + diagrams="on" + groups="on" + rawOutput="${scaladoc.raw.output}" + noPrefixes="${scaladoc.no.prefixes}"> <include name="**/*.scala"/> </scaladoc> <touch file="${build-docs.dir}/actors-migration.complete" verbose="no"/> diff --git a/lib/scala-compiler-src.jar.desired.sha1 b/lib/scala-compiler-src.jar.desired.sha1 index 48b3d3284e..6840b60528 100644 --- a/lib/scala-compiler-src.jar.desired.sha1 +++ b/lib/scala-compiler-src.jar.desired.sha1 @@ -1 +1 @@ -259fd9f0a50ed6003248a01a366a97a5549aa386 ?scala-compiler-src.jar +6a03de33fb670e1b1a9930234adb6d1d6435323d ?scala-compiler-src.jar diff --git a/lib/scala-compiler.jar.desired.sha1 b/lib/scala-compiler.jar.desired.sha1 index a8dbdb0a38..393820f481 100644 --- a/lib/scala-compiler.jar.desired.sha1 +++ b/lib/scala-compiler.jar.desired.sha1 @@ -1 +1 @@ -42f7367cc6ac59022d098e6091e5425390b9c925 ?scala-compiler.jar +cf6aab754bcf77ab433e2b05d1f452e13f24cedc ?scala-compiler.jar diff --git a/lib/scala-library-src.jar.desired.sha1 b/lib/scala-library-src.jar.desired.sha1 index 3379287733..cefff7eec9 100644 --- a/lib/scala-library-src.jar.desired.sha1 +++ b/lib/scala-library-src.jar.desired.sha1 @@ -1 +1 @@ -e31e38414fd19c10add3e65bf77c2fd7c6c26f7d ?scala-library-src.jar +02dcd8656f99eabbad8d9dce06a33d4f6f8d7c38 ?scala-library-src.jar diff --git a/lib/scala-library.jar.desired.sha1 b/lib/scala-library.jar.desired.sha1 index bef528ce26..f7f2321177 100644 --- a/lib/scala-library.jar.desired.sha1 +++ b/lib/scala-library.jar.desired.sha1 @@ -1 +1 @@ -2418c95bf7db34f87ebda4a5eaa918fe85047afb ?scala-library.jar +6a035e798a94c6ca051e6a7663293cfee3d7136f ?scala-library.jar diff --git a/lib/scala-reflect-src.jar.desired.sha1 b/lib/scala-reflect-src.jar.desired.sha1 index b3a5f03efe..fb09e9d585 100644 --- a/lib/scala-reflect-src.jar.desired.sha1 +++ b/lib/scala-reflect-src.jar.desired.sha1 @@ -1 +1 @@ -51c64d77ad4c4233a06cea7ea80b0fb77e9867c4 ?scala-reflect-src.jar +ecb9dd737935812a6399a3426e9126908ef870b2 ?scala-reflect-src.jar diff --git a/lib/scala-reflect.jar.desired.sha1 b/lib/scala-reflect.jar.desired.sha1 index 4d913d73ab..dc5b7abd0e 100644 --- a/lib/scala-reflect.jar.desired.sha1 +++ b/lib/scala-reflect.jar.desired.sha1 @@ -1 +1 @@ -5656bf2f17bb9f22b3ba61a83393a9794eaa5429 ?scala-reflect.jar +344f3feafa4bd155b13ba7a6ab3efd605f8f8388 ?scala-reflect.jar diff --git a/src/compiler/scala/reflect/makro/runtime/Context.scala b/src/compiler/scala/reflect/makro/runtime/Context.scala index c7563e7b67..68964b7abb 100644 --- a/src/compiler/scala/reflect/makro/runtime/Context.scala +++ b/src/compiler/scala/reflect/makro/runtime/Context.scala @@ -8,7 +8,6 @@ abstract class Context extends scala.reflect.makro.Context with CapturedVariables with Infrastructure with Enclosures - with Mirrors with Names with Reifiers with FrontEnds @@ -23,7 +22,7 @@ abstract class Context extends scala.reflect.makro.Context val universe: Global - val mirror: MirrorOf[universe.type] = new ContextMirror + val mirror: MirrorOf[universe.type] = universe.rootMirror val callsiteTyper: universe.analyzer.Typer diff --git a/src/compiler/scala/reflect/makro/runtime/ContextReifiers.scala b/src/compiler/scala/reflect/makro/runtime/ContextReifiers.scala deleted file mode 100644 index 564148fe6c..0000000000 --- a/src/compiler/scala/reflect/makro/runtime/ContextReifiers.scala +++ /dev/null @@ -1,26 +0,0 @@ -package scala.reflect.makro -package runtime - -abstract class ContextReifiers { self => - val c: Context - - import c.universe._ - import definitions._ - import treeBuild._ - - import scala.reflect.reify.Taggers - import language.implicitConversions - private implicit def context2taggers(c0: Context) : Taggers { val c: c0.type } = new { val c: c0.type = c0 } with Taggers - - private def forMacroContext[T](prefix: Tree)(op: (Tree, Tree) => T): T = { - val universe = gen.mkAttributedSelect(prefix.duplicate, MacroContextUniverse) setType SingleType(prefix.tpe, MacroContextUniverse) - val mirror = TypeApply(Select(Select(prefix.duplicate, nme.mirror), nme.asInstanceOf_), List(Select(Ident(nme.UNIVERSE_SHORT), tpnme.Mirror))) - op(universe, mirror) - } - - def materializeExprForMacroContext(prefix: Tree, expr: Tree): Tree = - forMacroContext(prefix)((universe, mirror) => c.materializeExpr(universe, mirror, expr)) - - def materializeTypeTagForMacroContext(prefix: Tree, tpe: Type, concrete: Boolean): Tree = - forMacroContext(prefix)((universe, mirror) => c.materializeTypeTag(universe, mirror, tpe, concrete)) -}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/makro/runtime/Mirrors.scala b/src/compiler/scala/reflect/makro/runtime/Mirrors.scala deleted file mode 100644 index ec970ee696..0000000000 --- a/src/compiler/scala/reflect/makro/runtime/Mirrors.scala +++ /dev/null @@ -1,43 +0,0 @@ -package scala.reflect.makro -package runtime - -import scala.tools.nsc.util.ScalaClassLoader - -trait Mirrors { - self: Context => - - import universe._ - import definitions._ - - class ContextMirror extends RootsBase(NoSymbol) { - val universe: self.universe.type = self.universe - def rootLoader: LazyType = rootMirror.rootLoader - - val RootPackage = rootMirror.RootPackage - val RootClass = rootMirror.RootClass - val EmptyPackage = rootMirror.EmptyPackage - val EmptyPackageClass = rootMirror.EmptyPackageClass - - // [Eugene++] this still doesn't solve the problem of invoking `c.typeCheck` on the code that refers to packageless symbols - override protected def mirrorMissingHook(owner: Symbol, name: Name): Symbol = { - if (owner.isRoot && isJavaClass(name.toString)) EmptyPackageClass.info decl name - else NoSymbol - } - - private lazy val libraryClasspathLoader: ClassLoader = { - val classpath = platform.classPath.asURLs - ScalaClassLoader.fromURLs(classpath) - } - - private def isJavaClass(path: String): Boolean = - try { - Class.forName(path, true, libraryClasspathLoader) - true - } catch { - case (_: ClassNotFoundException) | (_: NoClassDefFoundError) | (_: IncompatibleClassChangeError) => - false - } - - override def toString = "macro context mirror" - } -}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/Taggers.scala b/src/compiler/scala/reflect/reify/Taggers.scala index e09f13a052..4e30d0acf8 100644 --- a/src/compiler/scala/reflect/reify/Taggers.scala +++ b/src/compiler/scala/reflect/reify/Taggers.scala @@ -37,23 +37,21 @@ abstract class Taggers { } def materializeTypeTag(universe: Tree, mirror: Tree, tpe: Type, concrete: Boolean): Tree = { - if (universe.symbol == MacroContextUniverse && mirror == EmptyTree) { - import scala.reflect.makro.runtime.ContextReifiers - import language.implicitConversions - implicit def context2contextreifiers(c0: Context) : ContextReifiers { val c: c0.type } = new { val c: c0.type = c0 } with ContextReifiers - val Select(prefix, _) = universe - c.materializeTypeTagForMacroContext(prefix, tpe, concrete) - } else { - val tagType = if (concrete) TypeTagClass else AbsTypeTagClass - val unaffiliatedTagTpe = TypeRef(BaseUniverseClass.asTypeConstructor, tagType, List(tpe)) - val unaffiliatedTag = c.inferImplicitValue(unaffiliatedTagTpe, silent = true, withMacrosDisabled = true) - unaffiliatedTag match { - case success if !success.isEmpty => - Apply(Select(success, nme.in), List(mirror orElse mkDefaultMirrorRef(c.universe)(universe, c.callsiteTyper))) - case _ => - val tagModule = if (concrete) TypeTagModule else AbsTypeTagModule - materializeTag(universe, tpe, tagModule, c.reifyType(universe, mirror, tpe, concrete = concrete)) - } + val tagType = if (concrete) TypeTagClass else AbsTypeTagClass + // what we need here is to compose a type BaseUniverse # TypeTag[$tpe] + // to look for an implicit that conforms to this type + // that's why neither appliedType(tagType, List(tpe)) aka TypeRef(TypeTagsClass.thisType, tagType, List(tpe)) + // nor TypeRef(BaseUniverseClass.thisType, tagType, List(tpe)) won't fit here + // scala> :type -v def foo: scala.reflect.base.Universe#TypeTag[Int] = ??? + // NullaryMethodType(TypeRef(pre = TypeRef(TypeSymbol(Universe)), TypeSymbol(TypeTag), args = List($tpe)))) + val unaffiliatedTagTpe = TypeRef(BaseUniverseClass.typeConstructor, tagType, List(tpe)) + val unaffiliatedTag = c.inferImplicitValue(unaffiliatedTagTpe, silent = true, withMacrosDisabled = true) + unaffiliatedTag match { + case success if !success.isEmpty => + Apply(Select(success, nme.in), List(mirror orElse mkDefaultMirrorRef(c.universe)(universe, c.callsiteTyper))) + case _ => + val tagModule = if (concrete) TypeTagModule else AbsTypeTagModule + materializeTag(universe, tpe, tagModule, c.reifyType(universe, mirror, tpe, concrete = concrete)) } } diff --git a/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala index 9b0777580b..38c8fedac5 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala @@ -37,10 +37,40 @@ trait GenSymbols { mirrorMirrorSelect(nme.EmptyPackageClass) else if (sym.isModuleClass) Select(Select(reify(sym.sourceModule), nme.asModuleSymbol), nme.moduleClass) + else if (sym.isPackage) + mirrorMirrorCall(nme.staticPackage, reify(sym.fullName)) else if (sym.isLocatable) { - // [Eugene] am I doing this right? -// if (sym.isStaticOwner) { // no good for us, because it returns false for packages - if (sym.isStatic && (sym.isClass || sym.isModule)) { + /** This is a fancy conundrum that stems from the fact that Scala allows + * packageless packages and packageless objects with the same names in the same program. + * + * For more details read the docs to staticModule and staticPackage. + * Here I'll just provide the examples of how reify works for different kinds of symbols. + * + * // 1) packageless + * // packageless classes are non-ambiguous, but modules vs packages might be + * // that's why we have separate methods to reify those + * // note that staticModule will never resolve to a package if an object is missing and an homonymous package is present and vice versa + * // otherwise reification would be unsound + * class C => staticClass("C") + * object B => staticModule("B") + * package B => staticPackage("B") + * + * // 2) classes and modules enclosed in a package + * // staticXXX methods always look into parent packages and ignores parent modules, so for fully qualified names they are non-ambiguous + * // namely even if there's an object B { class C } next to package B { class C }, then staticClass("B.C") will resolve to a packageful class + * // this closely mirrors Scala's behavior, read up the docs to staticModule/staticPackage for more information + * package B { class C } => staticClass("B.C") + * package B { object B } => staticModule("B.B") + * package B { package B } => staticPackage("B.B") + * + * // 3) classes and modules enclosed in a packageless module + * // staticClass/staticModule won't look into EmptyPackageClass, so we reify such symbols in a roundabout way + * object B { class C } => selectType(staticModule("B"), "C") + * object B { object B } => selectType(staticModule("B"), "B") + * object B { package B } => impossible + */ + val hasPackagelessParent = sym.ownerChain.tail.tail exists (_.isEmptyPackageClass) + if (sym.isStatic && (sym.isClass || sym.isModule) && !hasPackagelessParent) { val resolver = if (sym.isType) nme.staticClass else nme.staticModule mirrorMirrorCall(resolver, reify(sym.fullName)) } else { diff --git a/src/compiler/scala/reflect/reify/codegen/GenTypes.scala b/src/compiler/scala/reflect/reify/codegen/GenTypes.scala index 82951a2434..c49e5b3342 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenTypes.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenTypes.scala @@ -41,8 +41,7 @@ trait GenTypes { case tpe @ ThisType(empty) if empty.isEmptyPackageClass => mirrorBuildCall(nme.thisPrefix, mirrorMirrorSelect(nme.EmptyPackageClass)) case tpe @ ThisType(clazz) if clazz.isModuleClass && clazz.isStatic => - // [Eugene++ to Martin] makes sense? - val module = mirrorMirrorCall(nme.staticModule, reify(clazz.fullName)) + val module = reify(clazz.sourceModule) val moduleClass = Select(Select(module, nme.asModuleSymbol), nme.moduleClass) mirrorFactoryCall(nme.ThisType, moduleClass) case tpe @ ThisType(_) => diff --git a/src/compiler/scala/reflect/reify/utils/Extractors.scala b/src/compiler/scala/reflect/reify/utils/Extractors.scala index 52e4ff08c1..86265ec77a 100644 --- a/src/compiler/scala/reflect/reify/utils/Extractors.scala +++ b/src/compiler/scala/reflect/reify/utils/Extractors.scala @@ -27,7 +27,7 @@ trait Extractors { // def applyImpl[U >: Nothing <: scala.reflect.api.Universe with Singleton]($m$untyped: scala.reflect.base.MirrorOf[U]): U#Tree = { // val $u: U = $m$untyped.universe; // val $m: $u.Mirror = $m$untyped.asInstanceOf[$u.Mirror]; - // $u.Apply($u.Select($u.Select($u.build.This($m.staticModule("scala.collection.immutable").moduleClass), $u.newTermName("List")), $u.newTermName("apply")), List($u.Literal($u.Constant(1)), $u.Literal($u.Constant(2)))) + // $u.Apply($u.Select($u.Select($u.build.This($m.staticPackage("scala.collection.immutable").moduleClass), $u.newTermName("List")), $u.newTermName("apply")), List($u.Literal($u.Constant(1)), $u.Literal($u.Constant(2)))) // } // }; // new $treecreator1() @@ -40,7 +40,7 @@ trait Extractors { // def apply[U >: Nothing <: scala.reflect.base.Universe with Singleton]($m$untyped: scala.reflect.base.MirrorOf[U]): U#Type = { // val $u: U = $m$untyped.universe; // val $m: $u.Mirror = $m$untyped.asInstanceOf[$u.Mirror]; - // $u.TypeRef($u.ThisType($m.staticModule("scala.collection.immutable").moduleClass), $m.staticClass("scala.collection.immutable.List"), List($m.staticClass("scala.Int").asTypeConstructor)) + // $u.TypeRef($u.ThisType($m.staticPackage("scala.collection.immutable").moduleClass), $m.staticClass("scala.collection.immutable.List"), List($m.staticClass("scala.Int").asTypeConstructor)) // } // }; // new $typecreator1() diff --git a/src/compiler/scala/tools/ant/FastScalac.scala b/src/compiler/scala/tools/ant/FastScalac.scala index c0d7441ad5..f146722087 100644 --- a/src/compiler/scala/tools/ant/FastScalac.scala +++ b/src/compiler/scala/tools/ant/FastScalac.scala @@ -159,7 +159,7 @@ class FastScalac extends Scalac { val url = ScalaClassLoader.originOfClass(classOf[FastScalac]).get File(url.getFile).jfile.getParentFile.getParentFile.getAbsolutePath } catch { - case _ => + case _: Throwable => buildError("Compilation failed because of an internal compiler error;"+ " couldn't determine value for -Dscala.home=<value>") } diff --git a/src/compiler/scala/tools/ant/Scalac.scala b/src/compiler/scala/tools/ant/Scalac.scala index e70716885e..c6809fb48e 100644 --- a/src/compiler/scala/tools/ant/Scalac.scala +++ b/src/compiler/scala/tools/ant/Scalac.scala @@ -99,7 +99,7 @@ class Scalac extends ScalaMatchingTask with ScalacShared { /** Defines valid values for the `target` property. */ object Target extends PermissibleValue { - val values = List("jvm-1.5", "jvm-1.6", "jvm-1.7", "msil") + val values = List("jvm-1.5", "jvm-1.5-fjbg", "jvm-1.5-asm", "jvm-1.6", "jvm-1.7", "msil") } /** Defines valid values for the `deprecation` and `unchecked` properties. */ diff --git a/src/compiler/scala/tools/ant/Scaladoc.scala b/src/compiler/scala/tools/ant/Scaladoc.scala index 2cada92c1e..b96ac6f29b 100644 --- a/src/compiler/scala/tools/ant/Scaladoc.scala +++ b/src/compiler/scala/tools/ant/Scaladoc.scala @@ -153,6 +153,11 @@ class Scaladoc extends ScalaMatchingTask { /** Instruct the scaladoc to produce textual ouput from html pages, for easy diff-ing */ private var docRawOutput: Boolean = false + /** Instruct the scaladoc not to generate prefixes */ + private var docNoPrefixes: Boolean = false + + /** Instruct the scaladoc tool to group similar functions together */ + private var docGroups: Boolean = false /*============================================================================*\ ** Properties setters ** @@ -427,6 +432,16 @@ class Scaladoc extends ScalaMatchingTask { def setRawOutput(input: String) = docRawOutput = Flag.getBooleanValue(input, "rawOutput") + /** Set the `noPrefixes` bit to prevent Scaladoc from generating prefixes in + * front of types -- may lead to confusion, but significantly speeds up the generation. + * @param input One of the flags `yes/no` or `on/off`. Default if no/off. */ + def setNoPrefixes(input: String) = + docNoPrefixes = Flag.getBooleanValue(input, "noPrefixes") + + /** Instruct the scaladoc tool to group similar functions together */ + def setGroups(input: String) = + docGroups = Flag.getBooleanValue(input, "groups") + /*============================================================================*\ ** Properties getters ** \*============================================================================*/ @@ -625,6 +640,8 @@ class Scaladoc extends ScalaMatchingTask { docSettings.docDiagrams.value = docDiagrams docSettings.docDiagramsDebug.value = docDiagramsDebug docSettings.docRawOutput.value = docRawOutput + docSettings.docNoPrefixes.value = docNoPrefixes + docSettings.docGroups.value = docGroups if(!docDiagramsDotPath.isEmpty) docSettings.docDiagramsDotPath.value = docDiagramsDotPath.get if (!docgenerator.isEmpty) docSettings.docgenerator.value = docgenerator.get @@ -658,15 +675,10 @@ class Scaladoc extends ScalaMatchingTask { "; see the documenter output for details.") reporter.printSummary() } catch { - case exception: Throwable if exception.getMessage ne null => - exception.printStackTrace() - safeBuildError("Document failed because of an internal documenter error (" + - exception.getMessage + "); see the error output for details.") - case exception => + case exception: Throwable => exception.printStackTrace() - safeBuildError("Document failed because of an internal documenter error " + - "(no error message provided); see the error output for details.") + val msg = Option(exception.getMessage) getOrElse "no error message provided" + safeBuildError(s"Document failed because of an internal documenter error ($msg); see the error output for details.") } } - } diff --git a/src/compiler/scala/tools/cmd/FromString.scala b/src/compiler/scala/tools/cmd/FromString.scala index 29f1baaa0c..415940b3fd 100644 --- a/src/compiler/scala/tools/cmd/FromString.scala +++ b/src/compiler/scala/tools/cmd/FromString.scala @@ -8,7 +8,7 @@ package cmd import nsc.io.{ Path, File, Directory } import scala.reflect.runtime.{universe => ru} -import scala.tools.reflect.StdTags._ +import scala.tools.reflect.StdRuntimeTags._ /** A general mechanism for defining how a command line argument * (always a String) is transformed into an arbitrary type. A few diff --git a/src/compiler/scala/tools/nsc/CompileServer.scala b/src/compiler/scala/tools/nsc/CompileServer.scala index fd59319c14..fb278db2bf 100644 --- a/src/compiler/scala/tools/nsc/CompileServer.scala +++ b/src/compiler/scala/tools/nsc/CompileServer.scala @@ -154,7 +154,7 @@ class StandardCompileServer extends SocketServer { case ex @ FatalError(msg) => reporter.error(null, "fatal error: " + msg) clearCompiler() - case ex => + case ex: Throwable => warn("Compile server encountered fatal condition: " + ex) shutdown = true throw ex diff --git a/src/compiler/scala/tools/nsc/Driver.scala b/src/compiler/scala/tools/nsc/Driver.scala index 15e2929ff1..1775602122 100644 --- a/src/compiler/scala/tools/nsc/Driver.scala +++ b/src/compiler/scala/tools/nsc/Driver.scala @@ -53,7 +53,7 @@ abstract class Driver { else doCompile(compiler) } catch { - case ex => + case ex: Throwable => compiler.logThrowable(ex) ex match { case FatalError(msg) => reporter.error(null, "fatal error: " + msg) diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index e378d71944..83335c4f62 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1447,6 +1447,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) settings.userSetSettings filter (_.isDeprecated) foreach { s => unit.deprecationWarning(NoPosition, s.name + " is deprecated: " + s.deprecationMessage.get) } + if (settings.target.value.contains("jvm-1.5")) + unit.deprecationWarning(NoPosition, settings.target.name + ":" + settings.target.value + " is deprecated: use target for Java 1.6 or above.") } /* An iterator returning all the units being compiled in this run */ diff --git a/src/compiler/scala/tools/nsc/ObjectRunner.scala b/src/compiler/scala/tools/nsc/ObjectRunner.scala index 110de7aad5..24ae0089a2 100644 --- a/src/compiler/scala/tools/nsc/ObjectRunner.scala +++ b/src/compiler/scala/tools/nsc/ObjectRunner.scala @@ -33,7 +33,7 @@ trait CommonRunner { */ def runAndCatch(urls: List[URL], objectName: String, arguments: Seq[String]): Either[Throwable, Boolean] = { try { run(urls, objectName, arguments) ; Right(true) } - catch { case e => Left(unwrap(e)) } + catch { case e: Throwable => Left(unwrap(e)) } } } diff --git a/src/compiler/scala/tools/nsc/ScriptRunner.scala b/src/compiler/scala/tools/nsc/ScriptRunner.scala index dd9bca4b7b..14c508548a 100644 --- a/src/compiler/scala/tools/nsc/ScriptRunner.scala +++ b/src/compiler/scala/tools/nsc/ScriptRunner.scala @@ -199,7 +199,7 @@ class ScriptRunner extends HasCompileSocket { scriptArgs: List[String]): Either[Throwable, Boolean] = { try Right(runScript(settings, scriptFile, scriptArgs)) - catch { case e => Left(unwrap(e)) } + catch { case e: Throwable => Left(unwrap(e)) } } /** Run a command diff --git a/src/compiler/scala/tools/nsc/ast/DocComments.scala b/src/compiler/scala/tools/nsc/ast/DocComments.scala index b545140c4a..d0d0b4b5f4 100755 --- a/src/compiler/scala/tools/nsc/ast/DocComments.scala +++ b/src/compiler/scala/tools/nsc/ast/DocComments.scala @@ -171,15 +171,15 @@ trait DocComments { self: Global => * 3. If there is no @return section in `dst` but there is one in `src`, copy it. */ def merge(src: String, dst: String, sym: Symbol, copyFirstPara: Boolean = false): String = { - val srcSections = tagIndex(src) - val dstSections = tagIndex(dst) - val srcParams = paramDocs(src, "@param", srcSections) - val dstParams = paramDocs(dst, "@param", dstSections) - val srcTParams = paramDocs(src, "@tparam", srcSections) - val dstTParams = paramDocs(dst, "@tparam", dstSections) - val out = new StringBuilder - var copied = 0 - var tocopy = startTag(dst, dstSections dropWhile (!isMovable(dst, _))) + val srcSections = tagIndex(src) + val dstSections = tagIndex(dst) + val srcParams = paramDocs(src, "@param", srcSections) + val dstParams = paramDocs(dst, "@param", dstSections) + val srcTParams = paramDocs(src, "@tparam", srcSections) + val dstTParams = paramDocs(dst, "@tparam", dstSections) + val out = new StringBuilder + var copied = 0 + var tocopy = startTag(dst, dstSections dropWhile (!isMovable(dst, _))) if (copyFirstPara) { val eop = // end of comment body (first para), which is delimited by blank line, or tag, or end of comment @@ -209,6 +209,7 @@ trait DocComments { self: Global => for (tparam <- sym.typeParams) mergeSection(srcTParams get tparam.name.toString, dstTParams get tparam.name.toString) mergeSection(returnDoc(src, srcSections), returnDoc(dst, dstSections)) + mergeSection(groupDoc(src, srcSections), groupDoc(dst, dstSections)) if (out.length == 0) dst else { @@ -457,22 +458,16 @@ trait DocComments { self: Global => case List() => NoType case site :: sites1 => select(site.thisType, name, findIn(sites1)) } - val (classes, pkgs) = site.ownerChain.span(!_.isPackageClass) - findIn(classes ::: List(pkgs.head, rootMirror.RootClass)) + // Previously, searching was taking place *only* in the current package and in the root package + // now we're looking for it everywhere in the hierarchy, so we'll be able to link variable expansions like + // immutable.Seq in package immutable + //val (classes, pkgs) = site.ownerChain.span(!_.isPackageClass) + //val sites = (classes ::: List(pkgs.head, rootMirror.RootClass))) + //findIn(sites) + findIn(site.ownerChain ::: List(definitions.EmptyPackage)) } - def getType(_str: String, variable: String): Type = { - /* - * work around the backticks issue suggested by Simon in - * https://groups.google.com/forum/?hl=en&fromgroups#!topic/scala-internals/z7s1CCRCz74 - * ideally, we'd have a removeWikiSyntax method in the CommentFactory to completely eliminate the wiki markup - */ - val str = - if (_str.length >= 2 && _str.startsWith("`") && _str.endsWith("`")) - _str.substring(1, _str.length - 2) - else - _str - + def getType(str: String, variable: String): Type = { def getParts(start: Int): List[String] = { val end = skipIdent(str, start) if (end == start) List() @@ -484,7 +479,7 @@ trait DocComments { self: Global => val parts = getParts(0) if (parts.isEmpty) { reporter.error(comment.codePos, "Incorrect variable expansion for " + variable + " in use case. Does the " + - "variable expand to wiki syntax when documenting " + site + "?") + "variable expand to wiki syntax when documenting " + site + "?") return ErrorType } val partnames = (parts.init map newTermName) :+ newTypeName(parts.last) @@ -498,25 +493,46 @@ trait DocComments { self: Global => case _ => (getSite(partnames.head), partnames.tail) } - (start /: rest)(select(_, _, NoType)) + val result = (start /: rest)(select(_, _, NoType)) + if (result == NoType) + reporter.warning(comment.codePos, "Could not find the type " + variable + " points to while expanding it " + + "for the usecase signature of " + sym + " in " + site + "." + + "In this context, " + variable + " = \"" + str + "\".") + result + } + + /** + * work around the backticks issue suggested by Simon in + * https://groups.google.com/forum/?hl=en&fromgroups#!topic/scala-internals/z7s1CCRCz74 + * ideally, we'd have a removeWikiSyntax method in the CommentFactory to completely eliminate the wiki markup + */ + def cleanupVariable(str: String) = { + val tstr = str.trim + if (tstr.length >= 2 && tstr.startsWith("`") && tstr.endsWith("`")) + tstr.substring(1, tstr.length - 1) + else + tstr } - val aliasExpansions: List[Type] = + // the Boolean tells us whether we can normalize: if we found an actual type, then yes, we can normalize, else no, + // use the synthetic alias created for the variable + val aliasExpansions: List[(Type, Boolean)] = for (alias <- aliases) yield lookupVariable(alias.name.toString.substring(1), site) match { case Some(repl) => - val tpe = getType(repl.trim, alias.name.toString) - if (tpe != NoType) tpe + val repl2 = cleanupVariable(repl) + val tpe = getType(repl2, alias.name.toString) + if (tpe != NoType) (tpe, true) else { - val alias1 = alias.cloneSymbol(rootMirror.RootClass, alias.rawflags, newTypeName(repl)) - typeRef(NoPrefix, alias1, Nil) + val alias1 = alias.cloneSymbol(rootMirror.RootClass, alias.rawflags, newTypeName(repl2)) + (typeRef(NoPrefix, alias1, Nil), false) } case None => - typeRef(NoPrefix, alias, Nil) + (typeRef(NoPrefix, alias, Nil), false) } - def subst(sym: Symbol, from: List[Symbol], to: List[Type]): Type = - if (from.isEmpty) sym.tpe + def subst(sym: Symbol, from: List[Symbol], to: List[(Type, Boolean)]): (Type, Boolean) = + if (from.isEmpty) (sym.tpe, false) else if (from.head == sym) to.head else subst(sym, from.tail, to.tail) @@ -524,8 +540,9 @@ trait DocComments { self: Global => def apply(tp: Type) = mapOver(tp) match { case tp1 @ TypeRef(pre, sym, args) if (sym.name.length > 1 && sym.name.startChar == '$') => subst(sym, aliases, aliasExpansions) match { - case TypeRef(pre1, sym1, _) => - typeRef(pre1, sym1, args) + case (TypeRef(pre1, sym1, _), canNormalize) => + val tpe = typeRef(pre1, sym1, args) + if (canNormalize) tpe.normalize else tpe case _ => tp1 } diff --git a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala index f88e41375d..e02d605965 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala @@ -140,7 +140,7 @@ abstract class TreeBrowsers { UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel") } catch { - case _ => UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()) + case _: Throwable => UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()) } val frame = new JFrame("Scala AST after " + phaseName + " phase") diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index e0c9631246..cde971085d 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1169,7 +1169,12 @@ self => if (inPattern) dropAnyBraces(pattern()) else { if (in.token == IDENTIFIER) atPos(in.offset)(Ident(ident())) - else expr() + else if(in.token == LBRACE) expr() + else if(in.token == THIS) { in.nextToken(); atPos(in.offset)(This(tpnme.EMPTY)) } + else { + syntaxErrorOrIncomplete("error in interpolated string: identifier or block expected", true) + EmptyTree + } } } } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 6ba273b8ea..7ccd6785bb 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -729,8 +729,12 @@ trait Scanners extends ScannersCommon { next.token = IDENTIFIER next.name = newTermName(cbuf.toString) cbuf.clear() + val idx = next.name.start - kwOffset + if (idx >= 0 && idx < kwArray.length) { + next.token = kwArray(idx) + } } else { - syntaxError("invalid string interpolation") + syntaxError("invalid string interpolation: `$$', `$'ident or `$'BlockExpr expected") } } else { val isUnclosedLiteral = !isUnicodeEscape && (ch == SU || (!multiLine && (ch == CR || ch == LF))) diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index b638745327..982267097b 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -113,26 +113,42 @@ abstract class GenICode extends SubComponent { m.native = m.symbol.hasAnnotation(definitions.NativeAttr) if (!m.isAbstractMethod && !m.native) { - ctx1 = genLoad(rhs, ctx1, m.returnType); - - // reverse the order of the local variables, to match the source-order - m.locals = m.locals.reverse - - rhs match { - case Block(_, Return(_)) => () - case Return(_) => () - case EmptyTree => - globalError("Concrete method has no definition: " + tree + ( - if (settings.debug.value) "(found: " + m.symbol.owner.info.decls.toList.mkString(", ") + ")" - else "") - ) - case _ => if (ctx1.bb.isEmpty) - ctx1.bb.closeWith(RETURN(m.returnType), rhs.pos) - else + if (m.symbol.isAccessor && m.symbol.accessed.hasStaticAnnotation) { + // in companion object accessors to @static fields, we access the static field directly + val hostClass = m.symbol.owner.companionClass + val staticfield = hostClass.info.findMember(m.symbol.accessed.name, NoFlags, NoFlags, false) + + if (m.symbol.isGetter) { + ctx1.bb.emit(LOAD_FIELD(staticfield, true) setHostClass hostClass, tree.pos) ctx1.bb.closeWith(RETURN(m.returnType)) + } else if (m.symbol.isSetter) { + ctx1.bb.emit(LOAD_LOCAL(m.locals.head), tree.pos) + ctx1.bb.emit(STORE_FIELD(staticfield, true), tree.pos) + ctx1.bb.closeWith(RETURN(m.returnType)) + } else assert(false, "unreachable") + } else { + ctx1 = genLoad(rhs, ctx1, m.returnType); + + // reverse the order of the local variables, to match the source-order + m.locals = m.locals.reverse + + rhs match { + case Block(_, Return(_)) => () + case Return(_) => () + case EmptyTree => + globalError("Concrete method has no definition: " + tree + ( + if (settings.debug.value) "(found: " + m.symbol.owner.info.decls.toList.mkString(", ") + ")" + else "") + ) + case _ => + if (ctx1.bb.isEmpty) + ctx1.bb.closeWith(RETURN(m.returnType), rhs.pos) + else + ctx1.bb.closeWith(RETURN(m.returnType)) + } + if (!ctx1.bb.closed) ctx1.bb.close + prune(ctx1.method) } - if (!ctx1.bb.closed) ctx1.bb.close - prune(ctx1.method) } else ctx1.method.setCode(NoCode) ctx1 @@ -854,9 +870,32 @@ abstract class GenICode extends SubComponent { generatedType = toTypeKind(fun.symbol.tpe.resultType) ctx1 + case app @ Apply(fun @ Select(qual, _), args) + if !ctx.method.symbol.isStaticConstructor + && fun.symbol.isAccessor && fun.symbol.accessed.hasStaticAnnotation => + // bypass the accessor to the companion object and load the static field directly + // the only place were this bypass is not done, is the static intializer for the static field itself + val sym = fun.symbol + generatedType = toTypeKind(sym.accessed.info) + val hostClass = qual.tpe.typeSymbol.orElse(sym.owner).companionClass + val staticfield = hostClass.info.findMember(sym.accessed.name, NoFlags, NoFlags, false) + + if (sym.isGetter) { + ctx.bb.emit(LOAD_FIELD(staticfield, true) setHostClass hostClass, tree.pos) + ctx + } else if (sym.isSetter) { + val ctx1 = genLoadArguments(args, sym.info.paramTypes, ctx) + ctx1.bb.emit(STORE_FIELD(staticfield, true), tree.pos) + ctx1.bb.emit(CONSTANT(Constant(false)), tree.pos) + ctx1 + } else { + assert(false, "supposedly unreachable") + ctx + } + case app @ Apply(fun, args) => val sym = fun.symbol - + if (sym.isLabel) { // jump to a label val label = ctx.labels.getOrElse(sym, { // it is a forward jump, scan for labels @@ -1623,8 +1662,12 @@ abstract class GenICode extends SubComponent { * backend emits them as static). * No code is needed for this module symbol. */ - for (f <- cls.info.decls ; if !f.isMethod && f.isTerm && !f.isModule) + for ( + f <- cls.info.decls; + if !f.isMethod && f.isTerm && !f.isModule && !(f.owner.isModuleClass && f.hasStaticAnnotation) + ) { ctx.clazz addField new IField(f) + } } /** diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 756d90bc53..025046f19e 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -598,7 +598,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { reverseJavaName.put(internalName, trackedSym) case Some(oldsym) => assert((oldsym == trackedSym) || (oldsym == RuntimeNothingClass) || (oldsym == RuntimeNullClass), // In contrast, neither NothingClass nor NullClass show up bytecode-level. - "how can getCommonSuperclass() do its job if different class symbols get the same bytecode-level internal name.") + "how can getCommonSuperclass() do its job if different class symbols get the same bytecode-level internal name: " + internalName) } } @@ -877,7 +877,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { def wrap(op: => Unit) = { try { op; true } - catch { case _ => false } + catch { case _: Throwable => false } } if (settings.Xverify.value) { @@ -1170,7 +1170,9 @@ abstract class GenASM extends SubComponent with BytecodeWriters { log("No forwarder for " + m + " due to conflict with " + linkedClass.info.member(m.name)) else { log("Adding static forwarder for '%s' from %s to '%s'".format(m, jclassName, moduleClass)) - addForwarder(isRemoteClass, jclass, moduleClass, m) + if (m.isAccessor && m.accessed.hasStaticAnnotation) { + log("@static: accessor " + m + ", accessed: " + m.accessed) + } else addForwarder(isRemoteClass, jclass, moduleClass, m) } } } @@ -1675,6 +1677,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { jmethod = clinitMethod jMethodName = CLASS_CONSTRUCTOR_NAME jmethod.visitCode() + computeLocalVarsIndex(m) genCode(m, false, true) jmethod.visitMaxs(0, 0) // just to follow protocol, dummy arguments jmethod.visitEnd() diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index 912a5b0e90..0ae2adac84 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -1021,6 +1021,8 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with method = m jmethod = clinitMethod + + computeLocalVarsIndex(m) genCode(m) case None => legacyStaticInitializer(cls, clinit) @@ -1114,7 +1116,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with linkedClass.info.members collect { case sym if sym.name.isTermName => sym.name } toSet } debuglog("Potentially conflicting names for forwarders: " + conflictingNames) - + for (m <- moduleClass.info.membersBasedOnFlags(ExcludedForwarderFlags, Flags.METHOD)) { if (m.isType || m.isDeferred || (m.owner eq ObjectClass) || m.isConstructor) debuglog("No forwarder for '%s' from %s to '%s'".format(m, className, moduleClass)) @@ -1122,7 +1124,9 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with log("No forwarder for " + m + " due to conflict with " + linkedClass.info.member(m.name)) else { log("Adding static forwarder for '%s' from %s to '%s'".format(m, className, moduleClass)) - addForwarder(jclass, moduleClass, m) + if (m.isAccessor && m.accessed.hasStaticAnnotation) { + log("@static: accessor " + m + ", accessed: " + m.accessed) + } else addForwarder(jclass, moduleClass, m) } } } @@ -1304,7 +1308,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with jclass.getType()) } } - + style match { case Static(true) => dbg("invokespecial"); jcode.emitINVOKESPECIAL(jowner, jname, jtype) case Static(false) => dbg("invokestatic"); jcode.emitINVOKESTATIC(jowner, jname, jtype) @@ -1885,7 +1889,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with */ def computeLocalVarsIndex(m: IMethod) { var idx = if (m.symbol.isStaticMember) 0 else 1; - + for (l <- m.params) { debuglog("Index value for " + l + "{" + l.## + "}: " + idx) l.index = idx diff --git a/src/compiler/scala/tools/nsc/doc/DocFactory.scala b/src/compiler/scala/tools/nsc/doc/DocFactory.scala index 3c92c3b4b6..27a03d5381 100644 --- a/src/compiler/scala/tools/nsc/doc/DocFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/DocFactory.scala @@ -48,8 +48,8 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor /** Creates a scaladoc site for all symbols defined in this call's `source`, * as well as those defined in `sources` of previous calls to the same processor. - * @param files The list of paths (relative to the compiler's source path, - * or absolute) of files to document. */ + * @param source The list of paths (relative to the compiler's source path, + * or absolute) of files to document or the source code. */ def makeUniverse(source: Either[List[String], String]): Option[Universe] = { assert(settings.docformat.value == "html") source match { @@ -81,9 +81,11 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor new { override val global: compiler.type = compiler } with model.ModelFactory(compiler, settings) with model.ModelFactoryImplicitSupport + with model.ModelFactoryTypeSupport with model.diagram.DiagramFactory with model.comment.CommentFactory - with model.TreeFactory { + with model.TreeFactory + with model.MemberLookup { override def templateShouldDocument(sym: compiler.Symbol, inTpl: TemplateImpl) = extraTemplatesToDocument(sym) || super.templateShouldDocument(sym, inTpl) } @@ -99,7 +101,6 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor println("no documentable class found in compilation units") None } - } object NoCompilerRunException extends ControlThrowable { } diff --git a/src/compiler/scala/tools/nsc/doc/Settings.scala b/src/compiler/scala/tools/nsc/doc/Settings.scala index 31e49131f6..ad178b8158 100644 --- a/src/compiler/scala/tools/nsc/doc/Settings.scala +++ b/src/compiler/scala/tools/nsc/doc/Settings.scala @@ -147,7 +147,7 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) val docDiagramsDotTimeout = IntSetting( "-diagrams-dot-timeout", - "The timeout before the graphviz dot util is forecefully closed, in seconds (default: 10)", + "The timeout before the graphviz dot util is forcefully closed, in seconds (default: 10)", 10, None, _ => None @@ -166,6 +166,33 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) "For each html file, create another .html.raw file containing only the text. (can be used for quickly diffing two scaladoc outputs)" ) + val docNoPrefixes = BooleanSetting ( + "-no-prefixes", + "Prevents generating prefixes in types, possibly creating ambiguous references, but significantly speeding up scaladoc." + ) + + val docNoLinkWarnings = BooleanSetting ( + "-no-link-warnings", + "Avoid warnings for ambiguous and incorrect links." + ) + + val docSkipPackages = StringSetting ( + "-skip-packages", + "<package1>:...:<packageN>", + "A colon-delimited list of fully qualified package names that will be skipped from scaladoc.", + "" + ) + + val docExpandAllTypes = BooleanSetting ( + "-expand-all-types", + "Expand all type aliases and abstract types into full template pages. (locally this can be done with the @template annotation)" + ) + + val docGroups = BooleanSetting ( + "-groups", + "Group similar functions together (based on the @group annotation)" + ) + // Somewhere slightly before r18708 scaladoc stopped building unless the // self-type check was suppressed. I hijacked the slotted-for-removal-anyway // suppress-vt-warnings option and renamed it for this purpose. @@ -177,7 +204,9 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) docDiagrams, docDiagramsDebug, docDiagramsDotPath, docDiagramsDotTimeout, docDiagramsDotRestart, docImplicits, docImplicitsDebug, docImplicitsShowAll, - docDiagramsMaxNormalClasses, docDiagramsMaxImplicitClasses + docDiagramsMaxNormalClasses, docDiagramsMaxImplicitClasses, + docNoPrefixes, docNoLinkWarnings, docRawOutput, docSkipPackages, + docExpandAllTypes, docGroups ) val isScaladocSpecific: String => Boolean = scaladocSpecific map (_.name) @@ -186,6 +215,15 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) // set by the testsuite, when checking test output var scaladocQuietRun = false + lazy val skipPackageNames = + if (docSkipPackages.value == "") + Set[String]() + else + docSkipPackages.value.toLowerCase.split(':').toSet + + def skipPackage(qname: String) = + skipPackageNames(qname.toLowerCase) + /** * This is the hardcoded area of Scaladoc. This is where "undesirable" stuff gets eliminated. I know it's not pretty, * but ultimately scaladoc has to be useful. :) @@ -198,16 +236,15 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) * the function result should be a humanly-understandable description of the type class */ val knownTypeClasses: Map[String, String => String] = Map() + - // TODO: Bring up to date and test these - ("scala.package.Numeric" -> ((tparam: String) => tparam + " is a numeric class, such as Int, Long, Float or Double")) + - ("scala.package.Integral" -> ((tparam: String) => tparam + " is an integral numeric class, such as Int or Long")) + - ("scala.package.Fractional" -> ((tparam: String) => tparam + " is a fractional numeric class, such as Float or Double")) + - ("scala.reflect.Manifest" -> ((tparam: String) => tparam + " is accompanied by a Manifest, which is a runtime representation of its type that survives erasure")) + - ("scala.reflect.ClassManifest" -> ((tparam: String) => tparam + " is accompanied by a ClassManifest, which is a runtime representation of its type that survives erasure")) + - ("scala.reflect.OptManifest" -> ((tparam: String) => tparam + " is accompanied by an OptManifest, which can be either a runtime representation of its type or the NoManifest, which means the runtime type is not available")) + - ("scala.reflect.ClassTag" -> ((tparam: String) => tparam + " is accompanied by a ClassTag, which is a runtime representation of its type that survives erasure")) + - ("scala.reflect.AbsTypeTag" -> ((tparam: String) => tparam + " is accompanied by an AbsTypeTag, which is a runtime representation of its type that survives erasure")) + - ("scala.reflect.TypeTag" -> ((tparam: String) => tparam + " is accompanied by a TypeTag, which is a runtime representation of its type that survives erasure")) + ("scala.math.Numeric" -> ((tparam: String) => tparam + " is a numeric class, such as Int, Long, Float or Double")) + + ("scala.math.Integral" -> ((tparam: String) => tparam + " is an integral numeric class, such as Int or Long")) + + ("scala.math.Fractional" -> ((tparam: String) => tparam + " is a fractional numeric class, such as Float or Double")) + + ("scala.reflect.Manifest" -> ((tparam: String) => tparam + " is accompanied by a Manifest, which is a runtime representation of its type that survives erasure")) + + ("scala.reflect.ClassManifest" -> ((tparam: String) => tparam + " is accompanied by a ClassManifest, which is a runtime representation of its type that survives erasure")) + + ("scala.reflect.OptManifest" -> ((tparam: String) => tparam + " is accompanied by an OptManifest, which can be either a runtime representation of its type or the NoManifest, which means the runtime type is not available")) + + ("scala.reflect.ClassTag" -> ((tparam: String) => tparam + " is accompanied by a ClassTag, which is a runtime representation of its type that survives erasure")) + + ("scala.reflect.AbsTypeTag" -> ((tparam: String) => tparam + " is accompanied by an AbsTypeTag, which is a runtime representation of its type that survives erasure")) + + ("scala.reflect.base.TypeTags.TypeTag" -> ((tparam: String) => tparam + " is accompanied by a TypeTag, which is a runtime representation of its type that survives erasure")) /** * Set of classes to exclude from index and diagrams diff --git a/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala b/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala index 51c5793d46..436425df83 100644 --- a/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala @@ -53,11 +53,16 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index) { "trait.png", "trait_big.png", "trait_diagram.png", + "type.png", + "type_big.png", + "type_diagram.png", "class_to_object_big.png", "object_to_class_big.png", - "object_to_trait_big.png", "trait_to_object_big.png", + "object_to_trait_big.png", + "type_to_object_big.png", + "object_to_type_big.png", "arrow-down.png", "arrow-right.png", @@ -138,7 +143,7 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index) { if (!(written contains tpl)) { writeForThis(new page.Template(universe, diagramGenerator, tpl)) written += tpl - tpl.templates map writeTemplate + tpl.templates collect { case d: DocTemplateEntity => d } map writeTemplate } } diff --git a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala index 4a1a8cf898..aa2df57967 100644 --- a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala +++ b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala @@ -122,17 +122,30 @@ abstract class HtmlPage extends Page { thisPage => case Text(text) => xml.Text(text) case Summary(in) => inlineToHtml(in) case HtmlTag(tag) => xml.Unparsed(tag) - case EntityLink(target, template) => template() match { - case Some(tpl) => - templateToHtml(tpl) - case None => - xml.Text(target) - } + case EntityLink(target, link) => linkToHtml(target, link, true) + } + + def linkToHtml(text: Inline, link: LinkTo, hasLinks: Boolean) = link match { + case LinkToTpl(dtpl) => + if (hasLinks) + <a href={ relativeLinkTo(dtpl) } class="extype" name={ dtpl.qualifiedName }>{ inlineToHtml(text) }</a> + else + <span class="extype" name={ dtpl.qualifiedName }>{ inlineToHtml(text) }</span> + case LinkToMember(mbr, inTpl) => + if (hasLinks) + <a href={ relativeLinkTo(inTpl) + "#" + mbr.signature } class="extmbr" name={ mbr.qualifiedName }>{ inlineToHtml(text) }</a> + else + <span class="extmbr" name={ mbr.qualifiedName }>{ inlineToHtml(text) }</span> + case Tooltip(tooltip) => + <span class="extype" name={ tooltip }>{ inlineToHtml(text) }</span> + // TODO: add case LinkToExternal here + case NoLink => + inlineToHtml(text) } def typeToHtml(tpes: List[model.TypeEntity], hasLinks: Boolean): NodeSeq = tpes match { case Nil => - sys.error("Internal Scaladoc error") + NodeSeq.Empty case List(tpe) => typeToHtml(tpe, hasLinks) case tpe :: rest => @@ -153,15 +166,9 @@ abstract class HtmlPage extends Page { thisPage => } } def toLinksIn(inPos: Int, starts: List[Int]): NodeSeq = { - val (tpl, width) = tpe.refEntity(inPos) - (tpl match { - case dtpl:DocTemplateEntity if hasLinks => - <a href={ relativeLinkTo(dtpl) } class="extype" name={ dtpl.qualifiedName }>{ - string.slice(inPos, inPos + width) - }</a> - case tpl => - <span class="extype" name={ tpl.qualifiedName }>{ string.slice(inPos, inPos + width) }</span> - }) ++ toLinksOut(inPos + width, starts.tail) + val (link, width) = tpe.refEntity(inPos) + val text = comment.Text(string.slice(inPos, inPos + width)) + linkToHtml(text, link, hasLinks) ++ toLinksOut(inPos + width, starts.tail) } if (hasLinks) toLinksOut(0, tpe.refEntity.keySet.toList) @@ -204,10 +211,12 @@ abstract class HtmlPage extends Page { thisPage => else if (ety.isTrait) "trait_big.png" else if (ety.isClass && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None) "class_to_object_big.png" else if (ety.isClass) "class_big.png" + else if ((ety.isAbstractType || ety.isAliasType) && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None) "type_to_object_big.png" + else if ((ety.isAbstractType || ety.isAliasType)) "type_big.png" else if (ety.isObject && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None && ety.companion.get.isClass) "object_to_class_big.png" else if (ety.isObject && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None && ety.companion.get.isTrait) "object_to_trait_big.png" + else if (ety.isObject && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None && (ety.companion.get.isAbstractType || ety.companion.get.isAliasType)) "object_to_trait_big.png" else if (ety.isObject) "object_big.png" else if (ety.isPackage) "package_big.png" else "class_big.png" // FIXME: an entity *should* fall into one of the above categories, but AnyRef is somehow not - } diff --git a/src/compiler/scala/tools/nsc/doc/html/Page.scala b/src/compiler/scala/tools/nsc/doc/html/Page.scala index 5e3ab87ccd..d30ca5dc08 100644 --- a/src/compiler/scala/tools/nsc/doc/html/Page.scala +++ b/src/compiler/scala/tools/nsc/doc/html/Page.scala @@ -48,13 +48,21 @@ abstract class Page { * @param generator The generator that is writing this page. */ def writeFor(site: HtmlFactory): Unit - def docEntityKindToString(ety: DocTemplateEntity) = - if (ety.isTrait) "trait" - else if (ety.isCaseClass) "case class" - else if (ety.isClass) "class" - else if (ety.isObject) "object" - else if (ety.isPackage) "package" - else "class" // FIXME: an entity *should* fall into one of the above categories, but AnyRef is somehow not + def kindToString(mbr: MemberEntity) = + mbr match { + case c: Class => if (c.isCaseClass) "case class" else "class" + case _: Trait => "trait" + case _: Package => "package" + case _: Object => "object" + case _: AbstractType => "type" + case _: AliasType => "type" + case _: Constructor => "new" + case v: Def => "def" + case v: Val if (v.isLazyVal) => "lazy val" + case v: Val if (v.isVal) => "val" + case v: Val if (v.isVar) => "var" + case _ => sys.error("Cannot create kind for: " + mbr + " of class " + mbr.getClass) + } def templateToPath(tpl: TemplateEntity): List[String] = { def doName(tpl: TemplateEntity): String = diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Index.scala b/src/compiler/scala/tools/nsc/doc/html/page/Index.scala index 0e894a03bf..b15d602b57 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Index.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Index.scala @@ -14,7 +14,7 @@ import scala.collection._ import scala.xml._ import scala.util.parsing.json.{JSONObject, JSONArray} -class Index(universe: doc.Universe, index: doc.Index) extends HtmlPage { +class Index(universe: doc.Universe, val index: doc.Index) extends HtmlPage { def path = List("index.html") @@ -61,12 +61,14 @@ class Index(universe: doc.Universe, index: doc.Index) extends HtmlPage { } <ol class="templates">{ val tpls: Map[String, Seq[DocTemplateEntity]] = - (pack.templates filter (t => !t.isPackage && !universe.settings.hardcoded.isExcluded(t.qualifiedName) )) groupBy (_.name) + (pack.templates collect { + case t: DocTemplateEntity if !t.isPackage && !universe.settings.hardcoded.isExcluded(t.qualifiedName) => t + }) groupBy (_.name) val placeholderSeq: NodeSeq = <div class="placeholder"></div> def createLink(entity: DocTemplateEntity, includePlaceholder: Boolean, includeText: Boolean) = { - val entityType = docEntityKindToString(entity) + val entityType = kindToString(entity) val linkContent = ( { if (includePlaceholder) placeholderSeq else NodeSeq.Empty } ++ diff --git a/src/compiler/scala/tools/nsc/doc/html/page/IndexScript.scala b/src/compiler/scala/tools/nsc/doc/html/page/IndexScript.scala index 2b68ac2937..a37dd3fb8b 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/IndexScript.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/IndexScript.scala @@ -27,7 +27,7 @@ class IndexScript(universe: doc.Universe, index: doc.Index) extends Page { val ary = merged.keys.toList.sortBy(_.toLowerCase).map(key => { val pairs = merged(key).map( - t => docEntityKindToString(t) -> relativeLinkTo(t) + t => kindToString(t) -> relativeLinkTo(t) ) :+ ("name" -> key) JSONObject(scala.collection.immutable.Map(pairs : _*)) @@ -62,7 +62,9 @@ class IndexScript(universe: doc.Universe, index: doc.Index) extends Page { def allPackagesWithTemplates = { Map(allPackages.map((key) => { - key -> key.templates.filter(t => !t.isPackage && !universe.settings.hardcoded.isExcluded(t.qualifiedName)) + key -> key.templates.collect { + case t: DocTemplateEntity if !t.isPackage && !universe.settings.hardcoded.isExcluded(t.qualifiedName) => t + } }) : _*) } } diff --git a/src/compiler/scala/tools/nsc/doc/html/page/ReferenceIndex.scala b/src/compiler/scala/tools/nsc/doc/html/page/ReferenceIndex.scala index a76cc231b4..effaee711d 100755 --- a/src/compiler/scala/tools/nsc/doc/html/page/ReferenceIndex.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/ReferenceIndex.scala @@ -34,7 +34,7 @@ class ReferenceIndex(letter: Char, index: doc.Index, universe: Universe) extends } else { html } - }) + }).toList.distinct <div class="entry"> <div class="name">{ diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala index 0d0410c7e2..422ea5ef1c 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala @@ -64,10 +64,10 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp nonAbsValueMembers partition (_.deprecation.isDefined) val (concValueMembers, shadowedImplicitMembers) = - nonDeprValueMembers partition (!Template.isShadowedOrAmbiguousImplicit(_)) + nonDeprValueMembers partition (!_.isShadowedOrAmbiguousImplicit) val typeMembers = - tpl.abstractTypes ++ tpl.aliasTypes ++ tpl.templates.filter(x => x.isTrait || x.isClass) sorted + tpl.abstractTypes ++ tpl.aliasTypes ++ tpl.templates.filter(x => x.isTrait || x.isClass) sorted (implicitly[Ordering[MemberEntity]]) val constructors = (tpl match { case cls: Class => (cls.constructors: List[MemberEntity]).sorted @@ -92,7 +92,7 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp <p id="owner">{ templatesToHtml(tpl.inTemplate.toRoot.reverse.tail, xml.Text(".")) }</p> } - <body class={ if (tpl.isTrait || tpl.isClass || tpl.qualifiedName == "scala.AnyRef") "type" else "value" }> + <body class={ if (tpl.isType) "type" else "value" }> <div id="definition"> { tpl.companion match { @@ -110,10 +110,26 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp <div id="mbrsel"> <div id='textfilter'><span class='pre'/><span class='input'><input id='mbrsel-input' type='text' accesskey='/'/></span><span class='post'/></div> - { if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty) NodeSeq.Empty else + { if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty && (!universe.settings.docGroups.value || (tpl.members.map(_.group).distinct.length == 1))) + NodeSeq.Empty + else <div id="order"> <span class="filtertype">Ordering</span> - <ol><li class="alpha in"><span>Alphabetic</span></li><li class="inherit out"><span>By inheritance</span></li></ol> + <ol> + { + if (!universe.settings.docGroups.value || (tpl.members.map(_.group).distinct.length == 1)) + NodeSeq.Empty + else + <li class="group out"><span>Grouped</span></li> + } + <li class="alpha in"><span>Alphabetic</span></li> + { + if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty) + NodeSeq.Empty + else + <li class="inherit out"><span>By inheritance</span></li> + } + </ol> </div> } { if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty) NodeSeq.Empty else @@ -223,6 +239,25 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp } </div> + <div id="groupedMembers"> + { + val allGroups = tpl.members.map(_.group).distinct + val orderedGroups = allGroups.map(group => (tpl.groupPriority(group), group)).sorted.map(_._2) + // linearization + NodeSeq fromSeq (for (group <- orderedGroups) yield + <div class="group" name={ group }> + <h3>{ tpl.groupName(group) }</h3> + { + tpl.groupDescription(group) match { + case Some(body) => <div class="comment cmt">{ bodyToHtml(body) }</div> + case _ => NodeSeq.Empty + } + } + </div> + ) + } + </div> + </div> <div id="tooltip" ></div> @@ -238,44 +273,13 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp </body> } - def boundsToString(hi: Option[TypeEntity], lo: Option[TypeEntity]): String = { - def bound0(bnd: Option[TypeEntity], pre: String): String = bnd match { - case None => "" - case Some(tpe) => pre ++ tpe.toString - } - bound0(hi, "<:") ++ bound0(lo, ">:") - } - - def tparamsToString(tpss: List[TypeParam]): String = { - if (tpss.isEmpty) "" else { - def tparam0(tp: TypeParam): String = - tp.variance + tp.name + boundsToString(tp.hi, tp.lo) - def tparams0(tpss: List[TypeParam]): String = (tpss: @unchecked) match { - case tp :: Nil => tparam0(tp) - case tp :: tps => tparam0(tp) ++ ", " ++ tparams0(tps) - } - "[" + tparams0(tpss) + "]" - } - } - - def defParamsToString(d: MemberEntity with Def): String = { - val paramLists: List[String] = - if (d.valueParams.isEmpty) Nil - else d.valueParams map (ps => ps map (_.resultType.name) mkString ("(",",",")")) - - tparamsToString(d.typeParams) + paramLists.mkString - } - def memberToHtml(mbr: MemberEntity, inTpl: DocTemplateEntity): NodeSeq = { - val defParamsString = mbr match { - case d:MemberEntity with Def => defParamsToString(d) - case _ => "" - } val memberComment = memberToCommentHtml(mbr, inTpl, false) <li name={ mbr.definitionName } visbl={ if (mbr.visibility.isProtected) "prt" else "pub" } data-isabs={ mbr.isAbstract.toString } - fullComment={ if(memberComment.filter(_.label=="div").isEmpty) "no" else "yes" }> - <a id={ mbr.name +defParamsString +":"+ mbr.resultType.name}/> + fullComment={ if(memberComment.filter(_.label=="div").isEmpty) "no" else "yes" } + group={ mbr.group }> + <a id={ mbr.signature }/> { signature(mbr, false) } { memberComment } </li> @@ -419,7 +423,7 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp { constraintText } </dd> } ++ { - if (Template.isShadowedOrAmbiguousImplicit(mbr)) { + if (mbr.isShadowedOrAmbiguousImplicit) { // These are the members that are shadowing or ambiguating the current implicit // see ImplicitMemberShadowing trait for more information val shadowingSuggestion = { @@ -434,10 +438,10 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp } val shadowingWarning: NodeSeq = - if (Template.isShadowedImplicit(mbr)) + if (mbr.isShadowedImplicit) xml.Text("This implicitly inherited member is shadowed by one or more members in this " + "class.") ++ shadowingSuggestion - else if (Template.isAmbiguousImplicit(mbr)) + else if (mbr.isAmbiguousImplicit) xml.Text("This implicitly inherited member is ambiguous. One or more implicitly " + "inherited members have similar signatures, so calling this member may produce an ambiguous " + "implicit conversion compiler error.") ++ shadowingSuggestion @@ -633,13 +637,13 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp } val typeHierarchy = if (s.docDiagrams.isSetByUser) mbr match { - case dtpl: DocTemplateEntity if isSelf && !isReduced && dtpl.inheritanceDiagram.isDefined => + case dtpl: DocTemplateEntity if isSelf && !isReduced => makeDiagramHtml(dtpl, dtpl.inheritanceDiagram, "Type Hierarchy", "inheritance-diagram") case _ => NodeSeq.Empty } else NodeSeq.Empty // diagrams not generated val contentHierarchy = if (s.docDiagrams.isSetByUser) mbr match { - case dtpl: DocTemplateEntity if isSelf && !isReduced && dtpl.contentDiagram.isDefined => + case dtpl: DocTemplateEntity if isSelf && !isReduced => makeDiagramHtml(dtpl, dtpl.contentDiagram, "Content Hierarchy", "content-diagram") case _ => NodeSeq.Empty } else NodeSeq.Empty // diagrams not generated @@ -647,19 +651,6 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp memberComment ++ paramComments ++ attributesBlock ++ linearization ++ subclasses ++ typeHierarchy ++ contentHierarchy } - def kindToString(mbr: MemberEntity): String = { - mbr match { - case tpl: DocTemplateEntity => docEntityKindToString(tpl) - case ctor: Constructor => "new" - case tme: MemberEntity => - ( if (tme.isDef) "def" - else if (tme.isVal) "val" - else if (tme.isLazyVal) "lazy val" - else if (tme.isVar) "var" - else "type") - } - } - def boundsToHtml(hi: Option[TypeEntity], lo: Option[TypeEntity], hasLinks: Boolean): NodeSeq = { def bound0(bnd: Option[TypeEntity], pre: String): NodeSeq = bnd match { case None => NodeSeq.Empty @@ -677,13 +668,13 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp case PrivateInTemplate(owner) if (owner == mbr.inTemplate) => Some(Paragraph(CText("private"))) case PrivateInTemplate(owner) => - Some(Paragraph(Chain(List(CText("private["), EntityLink(owner.qualifiedName, () => Some(owner)), CText("]"))))) + Some(Paragraph(Chain(List(CText("private["), EntityLink(comment.Text(owner.qualifiedName), LinkToTpl(owner)), CText("]"))))) case ProtectedInInstance() => Some(Paragraph(CText("protected[this]"))) case ProtectedInTemplate(owner) if (owner == mbr.inTemplate) => Some(Paragraph(CText("protected"))) case ProtectedInTemplate(owner) => - Some(Paragraph(Chain(List(CText("protected["), EntityLink(owner.qualifiedName, () => Some(owner)), CText("]"))))) + Some(Paragraph(Chain(List(CText("protected["), EntityLink(comment.Text(owner.qualifiedName), LinkToTpl(owner)), CText("]"))))) case Public() => None } @@ -700,8 +691,8 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp <span class="symbol"> { val nameClass = - if (Template.isImplicit(mbr)) - if (Template.isShadowedOrAmbiguousImplicit(mbr)) + if (mbr.isImplicitlyInherited) + if (mbr.isShadowedOrAmbiguousImplicit) "implicit shadowed" else "implicit" @@ -779,20 +770,21 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp } }{ if (isReduced) NodeSeq.Empty else { mbr match { - case tpl: DocTemplateEntity if !tpl.parentTypes.isEmpty => - <span class="result"> extends { typeToHtml(tpl.parentTypes.map(_._2), hasLinks) }</span> - case tme: MemberEntity if (tme.isDef || tme.isVal || tme.isLazyVal || tme.isVar) => <span class="result">: { typeToHtml(tme.resultType, hasLinks) }</span> - case abt: AbstractType => + case abt: MemberEntity with AbstractType => val b2s = boundsToHtml(abt.hi, abt.lo, hasLinks) if (b2s != NodeSeq.Empty) <span class="result">{ b2s }</span> else NodeSeq.Empty - case alt: AliasType => + case alt: MemberEntity with AliasType => <span class="result"> = { typeToHtml(alt.alias, hasLinks) }</span> + + case tpl: MemberTemplateEntity if !tpl.parentTypes.isEmpty => + <span class="result"> extends { typeToHtml(tpl.parentTypes.map(_._2), hasLinks) }</span> + case _ => NodeSeq.Empty } }} @@ -800,7 +792,7 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp </xml:group> mbr match { case dte: DocTemplateEntity if !isSelf => - <h4 class="signature">{ inside(hasLinks = false, nameLink = relativeLinkTo(dte)) }</h4> + <h4 class="signature">{ inside(hasLinks = true, nameLink = relativeLinkTo(dte)) }</h4> case _ if isSelf => <h4 id="signature" class="signature">{ inside(hasLinks = true) }</h4> case _ => @@ -850,18 +842,13 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp val link = relativeLinkTo(mbr) myXml ++= <span class="name"><a href={link}>{str.substring(from, to)}</a></span> case mbr: MemberEntity => - val anchor = "#" + mbr.name + defParamsString(mbr) + ":" + mbr.resultType.name + val anchor = "#" + mbr.signature val link = relativeLinkTo(mbr.inTemplate) myXml ++= <span class="name"><a href={link ++ anchor}>{str.substring(from, to)}</a></span> } index = to } } - // function used in the MemberEntity case above - def defParamsString(mbr: Entity):String = mbr match { - case d:MemberEntity with Def => defParamsToString(d) - case _ => "" - } if (index <= length-1) myXml ++= codeStringToXml(str.substring(index, length )) @@ -952,16 +939,18 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp } def makeDiagramHtml(tpl: DocTemplateEntity, diagram: Option[Diagram], description: String, id: String) = { - val s = universe.settings - val diagramSvg = generator.generate(diagram.get, tpl, this) - if (diagramSvg != NodeSeq.Empty) { - <div class="toggleContainer block diagram-container" id={ id + "-container"}> - <span class="toggle diagram-link">{ description }</span> - <a href="http://docs.scala-lang.org/overviews/scaladoc/usage.html#diagrams" target="_blank" class="diagram-help">Learn more about scaladoc diagrams</a> - <div class="diagram" id={ id }>{ - diagramSvg - }</div> - </div> + if (diagram.isDefined) { + val s = universe.settings + val diagramSvg = generator.generate(diagram.get, tpl, this) + if (diagramSvg != NodeSeq.Empty) { + <div class="toggleContainer block diagram-container" id={ id + "-container"}> + <span class="toggle diagram-link">{ description }</span> + <a href="http://docs.scala-lang.org/overviews/scaladoc/usage.html#diagrams" target="_blank" class="diagram-help">Learn more about scaladoc diagrams</a> + <div class="diagram" id={ id }>{ + diagramSvg + }</div> + </div> + } else NodeSeq.Empty } else NodeSeq.Empty } } @@ -970,12 +959,5 @@ object Template { /* Vlad: Lesson learned the hard way: don't put any stateful code that references the model here, * it won't be garbage collected and you'll end up filling the heap with garbage */ - def isImplicit(mbr: MemberEntity) = mbr.byConversion.isDefined - def isShadowedImplicit(mbr: MemberEntity): Boolean = - mbr.byConversion.map(_.source.implicitsShadowing.get(mbr).map(_.isShadowed).getOrElse(false)).getOrElse(false) - def isAmbiguousImplicit(mbr: MemberEntity): Boolean = - mbr.byConversion.map(_.source.implicitsShadowing.get(mbr).map(_.isAmbiguous).getOrElse(false)).getOrElse(false) - def isShadowedOrAmbiguousImplicit(mbr: MemberEntity) = isShadowedImplicit(mbr) || isAmbiguousImplicit(mbr) - def lowerFirstLetter(s: String) = if (s.length >= 1) s.substring(0,1).toLowerCase() + s.substring(1) else s } diff --git a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala index dc6f941c30..bae61f1a3b 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala @@ -25,7 +25,7 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator { // maps an index to its corresponding node private var index2Node: Map[Int, Node] = null // true if the current diagram is a class diagram - private var isClassDiagram = false + private var isInheritanceDiagram = false // incoming implicit nodes (needed for determining the CSS class of a node) private var incomingImplicitNodes: List[Node] = List() // the suffix used when there are two many classes to show @@ -66,15 +66,15 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator { var superClasses = List[Node]() var incomingImplicits = List[Node]() var outgoingImplicits = List[Node]() - isClassDiagram = false + isInheritanceDiagram = false d match { - case ClassDiagram(_thisNode, _superClasses, _subClasses, _incomingImplicits, _outgoingImplicits) => + case InheritanceDiagram(_thisNode, _superClasses, _subClasses, _incomingImplicits, _outgoingImplicits) => def textTypeEntity(text: String) = new TypeEntity { val name = text - def refEntity: SortedMap[Int, (TemplateEntity, Int)] = SortedMap() + def refEntity: SortedMap[Int, (LinkTo, Int)] = SortedMap() } // it seems dot chokes on node names over 8000 chars, so let's limit the size of the string @@ -86,29 +86,29 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator { // them by on node with a corresponding tooltip superClasses = if (_superClasses.length > settings.docDiagramsMaxNormalClasses.value) { val superClassesTooltip = Some(limitSize(_superClasses.map(_.tpe.name).mkString(", "))) - List(NormalNode(textTypeEntity(_superClasses.length + MultiSuffix), None, superClassesTooltip)) + List(NormalNode(textTypeEntity(_superClasses.length + MultiSuffix), None)(superClassesTooltip)) } else _superClasses subClasses = if (_subClasses.length > settings.docDiagramsMaxNormalClasses.value) { val subClassesTooltip = Some(limitSize(_subClasses.map(_.tpe.name).mkString(", "))) - List(NormalNode(textTypeEntity(_subClasses.length + MultiSuffix), None, subClassesTooltip)) + List(NormalNode(textTypeEntity(_subClasses.length + MultiSuffix), None)(subClassesTooltip)) } else _subClasses incomingImplicits = if (_incomingImplicits.length > settings.docDiagramsMaxImplicitClasses.value) { val incomingImplicitsTooltip = Some(limitSize(_incomingImplicits.map(_.tpe.name).mkString(", "))) - List(ImplicitNode(textTypeEntity(_incomingImplicits.length + MultiSuffix), None, incomingImplicitsTooltip)) + List(ImplicitNode(textTypeEntity(_incomingImplicits.length + MultiSuffix), None)(incomingImplicitsTooltip)) } else _incomingImplicits outgoingImplicits = if (_outgoingImplicits.length > settings.docDiagramsMaxImplicitClasses.value) { val outgoingImplicitsTooltip = Some(limitSize(_outgoingImplicits.map(_.tpe.name).mkString(", "))) - List(ImplicitNode(textTypeEntity(_outgoingImplicits.length + MultiSuffix), None, outgoingImplicitsTooltip)) + List(ImplicitNode(textTypeEntity(_outgoingImplicits.length + MultiSuffix), None)(outgoingImplicitsTooltip)) } else _outgoingImplicits thisNode = _thisNode nodes = List() edges = (thisNode -> superClasses) :: subClasses.map(_ -> List(thisNode)) node2Index = (thisNode::subClasses:::superClasses:::incomingImplicits:::outgoingImplicits).zipWithIndex.toMap - isClassDiagram = true + isInheritanceDiagram = true incomingImplicitNodes = incomingImplicits case _ => nodes = d.nodes @@ -119,7 +119,7 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator { index2Node = node2Index map {_.swap} val implicitsDot = { - if (!isClassDiagram) "" + if (!isInheritanceDiagram) "" else { // dot cluster containing thisNode val thisCluster = "subgraph clusterThis {\n" + @@ -242,6 +242,8 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator { attr ++= classStyle else if(node.isObjectNode) attr ++= objectStyle + else if(node.isTypeNode) + attr ++= typeStyle else attr ++= defaultStyle @@ -254,6 +256,8 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator { img = "class_diagram.png" else if(node.isObjectNode) img = "object_diagram.png" + else if(node.isTypeNode) + img = "type_diagram.png" if(!img.equals("")) { img = "<TD><IMG SCALE=\"TRUE\" SRC=\"" + settings.outdir.value + "/lib/" + img + "\" /></TD>" @@ -307,6 +311,8 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator { space + "trait" else if (node.isObjectNode) space + "object" + else if (node.isTypeNode) + space + "type" else default @@ -360,7 +366,7 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator { private def transform(e:scala.xml.Node): scala.xml.Node = e match { // add an id and class attribute to the SVG element case Elem(prefix, "svg", attribs, scope, child @ _*) => { - val klass = if (isClassDiagram) "class-diagram" else "package-diagram" + val klass = if (isInheritanceDiagram) "class-diagram" else "package-diagram" Elem(prefix, "svg", attribs, scope, child map(x => transform(x)) : _*) % new UnprefixedAttribute("id", "graph" + counter, Null) % new UnprefixedAttribute("class", klass, Null) @@ -491,6 +497,12 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator { "fontcolor" -> "#ffffff" ) + private val typeStyle = Map( + "color" -> "#115F3B", + "fillcolor" -> "#0A955B", + "fontcolor" -> "#ffffff" + ) + private def flatten(attributes: Map[String, String]) = attributes.map{ case (key, value) => key + "=\"" + value + "\"" }.mkString(", ") private val graphAttributesStr = graphAttributes.map{ case (key, value) => key + "=\"" + value + "\";\n" }.mkString diff --git a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala index 37600fa908..5cdd5c74a4 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala @@ -28,7 +28,7 @@ class DotRunner(settings: doc.Settings) { if (dotProcess == null) { if (dotRestarts < settings.docDiagramsDotRestart.value) { if (dotRestarts != 0) - settings.printMsg("A new graphviz dot process will be created...\n") + settings.printMsg("Graphviz will be restarted...\n") dotRestarts += 1 dotProcess = new DotProcess(settings) } else @@ -46,7 +46,7 @@ class DotRunner(settings: doc.Settings) { if (dotRestarts == settings.docDiagramsDotRestart.value) { settings.printMsg("\n") settings.printMsg("**********************************************************************") - settings.printMsg("Diagrams will be disabled for this run beucause the graphviz dot tool") + settings.printMsg("Diagrams will be disabled for this run because the graphviz dot tool") settings.printMsg("has malfunctioned too many times. These scaladoc flags may help:") settings.printMsg("") val baseList = List(settings.docDiagramsDebug, @@ -106,7 +106,7 @@ class DotProcess(settings: doc.Settings) { result } catch { - case exc => + case exc: Throwable => errorBuffer.append(" Main thread in " + templateName + ": " + (if (exc.isInstanceOf[NoSuchElementException]) "Timeout" else "Exception: " + exc)) error = true @@ -145,9 +145,10 @@ class DotProcess(settings: doc.Settings) { settings.printMsg("**********************************************************************") } else { // we shouldn't just sit there for 50s not reporting anything, no? - settings.printMsg("Graphviz dot encountered an error when generating the diagram for") - settings.printMsg(templateName + ". Use the " + settings.docDiagramsDebug.name + " flag") - settings.printMsg("for more information.") + settings.printMsg("Graphviz dot encountered an error when generating the diagram for:") + settings.printMsg(templateName) + settings.printMsg("These are usually spurious errors, but if you notice a persistant error on") + settings.printMsg("a diagram, please use the " + settings.docDiagramsDebug.name + " flag and report a bug with the output.") } } } @@ -173,7 +174,7 @@ class DotProcess(settings: doc.Settings) { } stdin.close() } catch { - case exc => + case exc: Throwable => error = true stdin.close() errorBuffer.append(" Input thread in " + templateName + ": Exception: " + exc + "\n") @@ -199,7 +200,7 @@ class DotProcess(settings: doc.Settings) { outputString.put(buffer.toString) stdOut.close() } catch { - case exc => + case exc: Throwable => error = true stdOut.close() errorBuffer.append(" Output thread in " + templateName + ": Exception: " + exc + "\n") @@ -218,7 +219,7 @@ class DotProcess(settings: doc.Settings) { } stdErr.close() } catch { - case exc => + case exc: Throwable => error = true stdErr.close() errorBuffer.append(" Error thread in " + templateName + ": Exception: " + exc + "\n") diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/diagrams.css b/src/compiler/scala/tools/nsc/doc/html/resource/lib/diagrams.css index 04d29580b7..5fe33f72f5 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/diagrams.css +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/diagrams.css @@ -119,6 +119,14 @@ svg.class-diagram .node.this.trait.over polygon fill: #235d7b; } +svg.package-diagram .node.type.over polygon, +svg.class-diagram .node.this.type.over polygon +{ + fill: #098552; + fill: #04663e; +} + + svg.package-diagram .node.object.over polygon { fill: #183377; diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js index eb7f752440..f29438edfb 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js @@ -15,15 +15,15 @@ var lastHash = ""; $(document).ready(function() { $('body').layout({ west__size: '20%' }); - $('#browser').layout({ + $('#browser').layout({ center__paneSelector: ".ui-west-center" //,center__initClosed:true ,north__paneSelector: ".ui-west-north" - }); + }); $('iframe').bind("load", function(){ var subtitle = $(this).contents().find('title').text(); $(document).attr('title', (title ? title + " - " : "") + subtitle); - + setUrlFragmentFromFrameSrc(); }); @@ -81,16 +81,16 @@ function setUrlFragmentFromFrameSrc() { var commonLength = location.pathname.lastIndexOf("/"); var frameLocation = frames["template"].location; var relativePath = frameLocation.pathname.slice(commonLength + 1); - + if(!relativePath || frameLocation.pathname.indexOf("/") < 0) return; - + // Add #, remove ".html" and replace "/" with "." fragment = "#" + relativePath.replace(/\.html$/, "").replace(/\//g, "."); - + // Add the frame's hash after an @ if(frameLocation.hash) fragment += ("@" + frameLocation.hash.slice(1)); - + // Use replace to not add history items lastFragment = fragment; location.replace(fragment); @@ -109,7 +109,7 @@ var Index = {}; if (type == 'object') { href = t['object']; } else { - href = t['class'] || t['trait'] || t['case class']; + href = t['class'] || t['trait'] || t['case class'] || t['type']; } return [ '<a class="tplshow" target="template" href="', @@ -142,10 +142,10 @@ var Index = {}; inner += openLink(template, 'object'); } - if (template['class'] || template['trait'] || template['case class']) { + if (template['class'] || template['trait'] || template['case class'] || template['type']) { inner += (inner == '') ? '<div class="placeholder" />' : '</a>'; - inner += openLink(template, template['trait'] ? 'trait' : 'class'); + inner += openLink(template, template['trait'] ? 'trait' : template['type'] ? 'type' : 'class'); } else { inner += '<div class="placeholder"/>'; } @@ -245,6 +245,7 @@ function configureEntityList() { function prepareEntityList() { var classIcon = $("#library > img.class"); var traitIcon = $("#library > img.trait"); + var typeIcon = $("#library > img.type"); var objectIcon = $("#library > img.object"); var packageIcon = $("#library > img.package"); @@ -252,6 +253,7 @@ function prepareEntityList() { $('#tpl li.pack').each(function () { $("span.class", this).each(function() { $(this).replaceWith(classIcon.clone()); }); $("span.trait", this).each(function() { $(this).replaceWith(traitIcon.clone()); }); + $("span.type", this).each(function() { $(this).replaceWith(typeIcon.clone()); }); $("span.object", this).each(function() { $(this).replaceWith(objectIcon.clone()); }); $("span.package", this).each(function() { $(this).replaceWith(packageIcon.clone()); }); }); @@ -265,11 +267,11 @@ function keyboardScrolldownLeftPane() { scheduler.add("init", function() { $("#textfilter input").blur(); var $items = $("#tpl li"); - $items.first().addClass('selected'); + $items.first().addClass('selected'); $(window).bind("keydown", function(e) { var $old = $items.filter('.selected'), - $new; + $new; switch ( e.keyCode ) { @@ -286,7 +288,7 @@ function keyboardScrolldownLeftPane() { case 27: // escape $old.removeClass('selected'); $(window).unbind(e); - $("#textfilter input").focus(); + $("#textfilter input").focus(); break; @@ -296,7 +298,7 @@ function keyboardScrolldownLeftPane() { if (!$new.length) { $new = $old.parent().prev(); } - + if ($new.is('ol') && $new.children(':last').is('ol')) { $new = $new.children().children(':last'); } else if ($new.is('ol')) { @@ -313,17 +315,17 @@ function keyboardScrolldownLeftPane() { if ($new.is('ol')) { $new = $new.children(':first'); } - break; - } - + break; + } + if ($new.is('li')) { $old.removeClass('selected'); - $new.addClass('selected'); + $new.addClass('selected'); } else if (e.keyCode == 38) { $(window).unbind(e); $("#textfilter input").focus(); } - }); + }); }); } @@ -342,13 +344,13 @@ function configureTextFilter() { $("#template").contents().find("#mbrsel-input").focus(); input.attr("value", ""); return false; - } + } if (event.keyCode == 40) { // down arrow $(window).unbind("keydown"); keyboardScrolldownLeftPane(); return false; - } - textFilter(); + } + textFilter(); }); input.focus(function(event) { input.select(); }); }); @@ -419,7 +421,7 @@ function textFilter() { }); configureHideFilter(); }; - + scheduler.add('filter', searchLoop); } diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/object_to_type_big.png b/src/compiler/scala/tools/nsc/doc/html/resource/lib/object_to_type_big.png Binary files differnew file mode 100644 index 0000000000..7502942eb6 --- /dev/null +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/object_to_type_big.png diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css index 1bee55313b..6fb7953724 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css @@ -253,6 +253,17 @@ dl.attributes > dd { color: white; } +#groupedMembers > div.group > h3 { + background: #dadada url("typebg.gif") repeat-x bottom left; /* green */ + height: 17px; + font-size: 12pt; +} + +#groupedMembers > div.group > h3 * { + color: white; +} + + /* Member cells */ div.members > ol { @@ -516,6 +527,20 @@ div.members > ol > li:last-child { /* Comments structured layout */ +.group > div.comment { + padding-top: 5px; + padding-bottom: 5px; + padding-right: 5px; + padding-left: 5px; + border: 1px solid #ddd; + background-color: #eeeee; + margin-top:5px; + margin-bottom:5px; + margin-right:5px; + margin-left:5px; + display: block; +} + p.comment { display: block; margin-left: 14.7em; diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js index c418c3280b..afd0293fe1 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js @@ -43,20 +43,20 @@ $(document).ready(function(){ case 33: //page up input.val(""); - filter(false); + filter(false); break; case 34: //page down input.val(""); - filter(false); - break; + filter(false); + break; - default: + default: window.scrollTo(0, $("#mbrsel").offset().top); - filter(true); + filter(true); break; - } + } }); input.focus(function(event) { input.select(); @@ -70,7 +70,7 @@ $(document).ready(function(){ if (event.keyCode == 9) { // tab $("#index-input", window.parent.document).focus(); input.attr("value", ""); - return false; + return false; } }); @@ -135,18 +135,21 @@ $(document).ready(function(){ }); $("#order > ol > li.alpha").click(function() { if ($(this).hasClass("out")) { - $(this).removeClass("out").addClass("in"); - $("#order > ol > li.inherit").removeClass("in").addClass("out"); orderAlpha(); }; }) $("#order > ol > li.inherit").click(function() { if ($(this).hasClass("out")) { - $(this).removeClass("out").addClass("in"); - $("#order > ol > li.alpha").removeClass("in").addClass("out"); orderInherit(); }; }); + $("#order > ol > li.group").click(function() { + if ($(this).hasClass("out")) { + orderGroup(); + }; + }); + $("#groupedMembers").hide(); + initInherit(); // Create tooltips @@ -202,9 +205,14 @@ $(document).ready(function(){ // Set parent window title windowTitle(); + + if ($("#order > ol > li.group").length == 1) { orderGroup(); }; }); function orderAlpha() { + $("#order > ol > li.alpha").removeClass("out").addClass("in"); + $("#order > ol > li.inherit").removeClass("in").addClass("out"); + $("#order > ol > li.group").removeClass("in").addClass("out"); $("#template > div.parent").hide(); $("#template > div.conversion").hide(); $("#mbrsel > div[id=ancestors]").show(); @@ -212,12 +220,25 @@ function orderAlpha() { }; function orderInherit() { + $("#order > ol > li.inherit").removeClass("out").addClass("in"); + $("#order > ol > li.alpha").removeClass("in").addClass("out"); + $("#order > ol > li.group").removeClass("in").addClass("out"); $("#template > div.parent").show(); $("#template > div.conversion").show(); $("#mbrsel > div[id=ancestors]").hide(); filter(); }; +function orderGroup() { + $("#order > ol > li.group").removeClass("out").addClass("in"); + $("#order > ol > li.alpha").removeClass("in").addClass("out"); + $("#order > ol > li.inherit").removeClass("in").addClass("out"); + $("#template > div.parent").hide(); + $("#template > div.conversion").hide(); + $("#mbrsel > div[id=ancestors]").show(); + filter(); +}; + /** Prepares the DOM for inheritance-based display. To do so it will: * - hide all statically-generated parents headings; * - copy all members from the value and type members lists (flat members) to corresponding lists nested below the @@ -225,44 +246,74 @@ function orderInherit() { * - initialises a control variable used by the filter method to control whether filtering happens on flat members * or on inheritance-grouped members. */ function initInherit() { - // parents is a map from fully-qualified names to the DOM node of parent headings. - var parents = new Object(); + // inheritParents is a map from fully-qualified names to the DOM node of parent headings. + var inheritParents = new Object(); + var groupParents = new Object(); $("#inheritedMembers > div.parent").each(function(){ - parents[$(this).attr("name")] = $(this); + inheritParents[$(this).attr("name")] = $(this); }); $("#inheritedMembers > div.conversion").each(function(){ - parents[$(this).attr("name")] = $(this); + inheritParents[$(this).attr("name")] = $(this); + }); + $("#groupedMembers > div.group").each(function(){ + groupParents[$(this).attr("name")] = $(this); }); + $("#types > ol > li").each(function(){ var mbr = $(this); this.mbrText = mbr.find("> .fullcomment .cmt").text(); var qualName = mbr.attr("name"); var owner = qualName.slice(0, qualName.indexOf("#")); var name = qualName.slice(qualName.indexOf("#") + 1); - var parent = parents[owner]; - if (parent != undefined) { - var types = $("> .types > ol", parent); + var inheritParent = inheritParents[owner]; + if (inheritParent != undefined) { + var types = $("> .types > ol", inheritParent); + if (types.length == 0) { + inheritParent.append("<div class='types members'><h3>Type Members</h3><ol></ol></div>"); + types = $("> .types > ol", inheritParent); + } + var clone = mbr.clone(); + clone[0].mbrText = this.mbrText; + types.append(clone); + } + var group = mbr.attr("group") + var groupParent = groupParents[group]; + if (groupParent != undefined) { + var types = $("> .types > ol", groupParent); if (types.length == 0) { - parent.append("<div class='types members'><h3>Type Members</h3><ol></ol></div>"); - types = $("> .types > ol", parent); + groupParent.append("<div class='types members'><ol></ol></div>"); + types = $("> .types > ol", groupParent); } var clone = mbr.clone(); clone[0].mbrText = this.mbrText; types.append(clone); } }); + $("#values > ol > li").each(function(){ var mbr = $(this); this.mbrText = mbr.find("> .fullcomment .cmt").text(); var qualName = mbr.attr("name"); var owner = qualName.slice(0, qualName.indexOf("#")); var name = qualName.slice(qualName.indexOf("#") + 1); - var parent = parents[owner]; - if (parent != undefined) { - var values = $("> .values > ol", parent); + var inheritParent = inheritParents[owner]; + if (inheritParent != undefined) { + var values = $("> .values > ol", inheritParent); if (values.length == 0) { - parent.append("<div class='values members'><h3>Value Members</h3><ol></ol></div>"); - values = $("> .values > ol", parent); + inheritParent.append("<div class='values members'><h3>Value Members</h3><ol></ol></div>"); + values = $("> .values > ol", inheritParent); + } + var clone = mbr.clone(); + clone[0].mbrText = this.mbrText; + values.append(clone); + } + var group = mbr.attr("group") + var groupParent = groupParents[group]; + if (groupParent != undefined) { + var values = $("> .values > ol", groupParent); + if (values.length == 0) { + groupParent.append("<div class='values members'><ol></ol></div>"); + values = $("> .values > ol", groupParent); } var clone = mbr.clone(); clone[0].mbrText = this.mbrText; @@ -275,6 +326,9 @@ function initInherit() { $("#inheritedMembers > div.conversion").each(function() { if ($("> div.members", this).length == 0) { $(this).remove(); }; }); + $("#groupedMembers > div.group").each(function() { + if ($("> div.members", this).length == 0) { $(this).remove(); }; + }); }; /* filter used to take boolean scrollToMember */ @@ -284,26 +338,43 @@ function filter() { var queryRegExp = new RegExp(query, "i"); var privateMembersHidden = $("#visbl > ol > li.public").hasClass("in"); var orderingAlphabetic = $("#order > ol > li.alpha").hasClass("in"); - var hiddenSuperclassElementsLinearization = orderingAlphabetic ? $("#linearization > li.out") : $("#linearization > li:gt(0)"); + var orderingInheritance = $("#order > ol > li.inherit").hasClass("in"); + var orderingGroups = $("#order > ol > li.group").hasClass("in"); + var hiddenSuperclassElementsLinearization = orderingInheritance ? $("#linearization > li:gt(0)") : $("#linearization > li.out"); var hiddenSuperclassesLinearization = hiddenSuperclassElementsLinearization.map(function() { return $(this).attr("name"); }).get(); - var hiddenSuperclassElementsImplicits = orderingAlphabetic ? $("#implicits > li.out") : $("#implicits > li"); + var hiddenSuperclassElementsImplicits = orderingInheritance ? $("#implicits > li") : $("#implicits > li.out"); var hiddenSuperclassesImplicits = hiddenSuperclassElementsImplicits.map(function() { return $(this).attr("name"); }).get(); var hideInheritedMembers; - if(orderingAlphabetic) { + if (orderingAlphabetic) { + $("#allMembers").show(); $("#inheritedMembers").hide(); + $("#groupedMembers").hide(); hideInheritedMembers = true; $("#allMembers > .members").each(filterFunc); - } - else { - $("#inheritedMembers").show(); + } else if (orderingGroups) { + $("#groupedMembers").show(); + $("#inheritedMembers").hide(); + $("#allMembers").hide(); hideInheritedMembers = true; - $("#allMembers > .members").each(filterFunc); + $("#groupedMembers > .group > .members").each(filterFunc); + $("#groupedMembers > div.group").each(function() { + $(this).show(); + if ($("> div.members", this).not(":hidden").length == 0) { + $(this).hide(); + } else { + $(this).show(); + } + }); + } else if (orderingInheritance) { + $("#inheritedMembers").show(); + $("#groupedMembers").hide(); + $("#allMembers").hide(); hideInheritedMembers = false; $("#inheritedMembers > .parent > .members").each(filterFunc); $("#inheritedMembers > .conversion > .members").each(filterFunc); diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/type.png b/src/compiler/scala/tools/nsc/doc/html/resource/lib/type.png Binary files differnew file mode 100644 index 0000000000..366ec4e992 --- /dev/null +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/type.png diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/type_big.png b/src/compiler/scala/tools/nsc/doc/html/resource/lib/type_big.png Binary files differnew file mode 100644 index 0000000000..df0dc118bf --- /dev/null +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/type_big.png diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/type_diagram.png b/src/compiler/scala/tools/nsc/doc/html/resource/lib/type_diagram.png Binary files differnew file mode 100644 index 0000000000..d6fbb84ff2 --- /dev/null +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/type_diagram.png diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/type_to_object_big.png b/src/compiler/scala/tools/nsc/doc/html/resource/lib/type_to_object_big.png Binary files differnew file mode 100644 index 0000000000..1bd2833a63 --- /dev/null +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/type_to_object_big.png diff --git a/src/compiler/scala/tools/nsc/doc/model/Entity.scala b/src/compiler/scala/tools/nsc/doc/model/Entity.scala index 2901daafd6..16ade0787e 100644 --- a/src/compiler/scala/tools/nsc/doc/model/Entity.scala +++ b/src/compiler/scala/tools/nsc/doc/model/Entity.scala @@ -53,6 +53,15 @@ trait Entity { /** The kind of the entity */ def kind: String + + /** Whether or not the template was defined in a package object */ + def inPackageObject: Boolean + + /** Indicates whether this entity lives in the types namespace (classes, traits, abstract/alias types) */ + def isType: Boolean + + /** Indicates whether this entity lives in the terms namespace (objects, packages, methods, values) */ + def isTerm: Boolean } object Entity { @@ -88,17 +97,14 @@ trait TemplateEntity extends Entity { /** Whether documentation is available for this template. */ def isDocTemplate: Boolean + /** Whether documentation is available for this template. */ + def isNoDocMemberTemplate: Boolean + /** Whether this template is a case class. */ def isCaseClass: Boolean - /** Whether or not the template was defined in a package object */ - def inPackageObject: Boolean - /** The self-type of this template, if it differs from the template type. */ def selfType : Option[TypeEntity] - - /** The type of this entity, with type members */ - def ownType: TypeEntity } @@ -110,6 +116,9 @@ trait MemberEntity extends Entity { /** The comment attached to this member, if any. */ def comment: Option[Comment] + /** The group this member is from */ + def group: String + /** The template of which this entity is a member. */ def inTemplate: DocTemplateEntity @@ -179,11 +188,28 @@ trait MemberEntity extends Entity { /** If this symbol is a use case, the useCaseOf will contain the member it was derived from, containing the full * signature and the complete parameter descriptions. */ - def useCaseOf: Option[MemberEntity] = None + def useCaseOf: Option[MemberEntity] /** If this member originates from an implicit conversion, we set the implicit information to the correct origin */ def byConversion: Option[ImplicitConversion] + + /** The identity of this member, used for linking */ + def signature: String + + /** Indicates whether the member is inherited by implicit conversion */ + def isImplicitlyInherited: Boolean + + /** Indicates whether there is another member with the same name in the template that will take precendence */ + def isShadowedImplicit: Boolean + + /** Indicates whether there are other implicitly inherited members that have similar signatures (and thus they all + * become ambiguous) */ + def isAmbiguousImplicit: Boolean + + /** Indicates whether the implicitly inherited member is shadowed or ambiguous in its template */ + def isShadowedOrAmbiguousImplicit: Boolean } + object MemberEntity { // Oh contravariance, contravariance, wherefore art thou contravariance? // Note: the above works for both the commonly misunderstood meaning of the line and the real one. @@ -195,24 +221,38 @@ trait HigherKinded { /** The type parameters of this entity. */ def typeParams: List[TypeParam] - } /** A template (class, trait, object or package) which is referenced in the universe, but for which no further * documentation is available. Only templates for which a source file is given are documented by Scaladoc. */ trait NoDocTemplate extends TemplateEntity { - def kind = "<no doc>" + def kind = + if (isClass) "class" + else if (isTrait) "trait" + else if (isObject) "object" + else "" } -/** TODO: Document */ -trait NoDocTemplateMemberEntity extends TemplateEntity with MemberEntity { - def kind = "<no doc, mbr>" +/** An inherited template that was not documented in its original owner - example: + * in classpath: trait T { class C } -- T (and implicitly C) are not documented + * in the source: trait U extends T -- C appears in U as a MemberTemplateImpl + * -- that is, U has a member for it but C doesn't get its own page */ +trait MemberTemplateEntity extends TemplateEntity with MemberEntity with HigherKinded { + + /** The value parameters of this case class, or an empty list if this class is not a case class. As case class value + * parameters cannot be curried, the outer list has exactly one element. */ + def valueParams: List[List[ValueParam]] + + /** The direct super-type of this template + e.g: {{{class A extends B[C[Int]] with D[E]}}} will have two direct parents: class B and D + NOTE: we are dropping the refinement here! */ + def parentTypes: List[(TemplateEntity, TypeEntity)] } /** A template (class, trait, object or package) for which documentation is available. Only templates for which * a source file is given are documented by Scaladoc. */ -trait DocTemplateEntity extends TemplateEntity with MemberEntity { +trait DocTemplateEntity extends MemberTemplateEntity { /** The list of templates such that each is a member of the template that follows it; the first template is always * this template, the last the root package entity. */ @@ -226,11 +266,6 @@ trait DocTemplateEntity extends TemplateEntity with MemberEntity { * only if the `docsourceurl` setting has been set. */ def sourceUrl: Option[java.net.URL] - /** The direct super-type of this template - e.g: {{{class A extends B[C[Int]] with D[E]}}} will have two direct parents: class B and D - NOTE: we are dropping the refinement here! */ - def parentTypes: List[(TemplateEntity, TypeEntity)] - /** All class, trait and object templates which are part of this template's linearization, in lineratization order. * This template's linearization contains all of its direct and indirect super-classes and super-traits. */ def linearizationTemplates: List[TemplateEntity] @@ -253,7 +288,7 @@ trait DocTemplateEntity extends TemplateEntity with MemberEntity { /** All templates that are members of this template. If this template is a package, only templates for which * documentation is available in the universe (`DocTemplateEntity`) are listed. */ - def templates: List[DocTemplateEntity] + def templates: List[TemplateEntity with MemberEntity] /** All methods that are members of this template. */ def methods: List[Def] @@ -267,6 +302,12 @@ trait DocTemplateEntity extends TemplateEntity with MemberEntity { /** All type aliases that are members of this template. */ def aliasTypes: List[AliasType] + /** The primary constructor of this class, if it has been defined. */ + def primaryConstructor: Option[Constructor] + + /** All constructors of this class, including the primary constructor. */ + def constructors: List[Constructor] + /** The companion of this template, or none. If a class and an object are defined as a pair of the same name, the * other entity of the pair is the companion. */ def companion: Option[DocTemplateEntity] @@ -289,41 +330,35 @@ trait DocTemplateEntity extends TemplateEntity with MemberEntity { /** If this template contains other templates, such as classes and traits, they will be shown in this diagram */ def contentDiagram: Option[Diagram] -} + /** Returns the group description taken either from this template or its linearizationTypes */ + def groupDescription(group: String): Option[Body] + + /** Returns the group description taken either from this template or its linearizationTypes */ + def groupPriority(group: String): Int + + /** Returns the group description taken either from this template or its linearizationTypes */ + def groupName(group: String): String +} /** A trait template. */ -trait Trait extends DocTemplateEntity with HigherKinded { +trait Trait extends MemberTemplateEntity { def kind = "trait" } - /** A class template. */ -trait Class extends Trait with HigherKinded { - - /** The primary constructor of this class, if it has been defined. */ - def primaryConstructor: Option[Constructor] - - /** All constructors of this class, including the primary constructor. */ - def constructors: List[Constructor] - - /** The value parameters of this case class, or an empty list if this class is not a case class. As case class value - * parameters cannot be curried, the outer list has exactly one element. */ - def valueParams: List[List[ValueParam]] - +trait Class extends MemberTemplateEntity { override def kind = "class" } - /** An object template. */ -trait Object extends DocTemplateEntity { +trait Object extends MemberTemplateEntity { def kind = "object" } - /** A package template. A package is in the universe if it is declared as a package object, or if it * contains at least one template. */ -trait Package extends Object { +trait Package extends DocTemplateEntity { /** The package of which this package is a member. */ def inTemplate: Package @@ -354,7 +389,6 @@ trait NonTemplateMemberEntity extends MemberEntity { /** Whether this member is a bridge member. A bridge member does only exist for binary compatibility reasons * and should not appear in ScalaDoc. */ def isBridge: Boolean - } @@ -391,7 +425,7 @@ trait Val extends NonTemplateMemberEntity { /** An abstract type member of a template. */ -trait AbstractType extends NonTemplateMemberEntity with HigherKinded { +trait AbstractType extends MemberTemplateEntity with HigherKinded { /** The lower bound for this abstract type, if it has been defined. */ def lo: Option[TypeEntity] @@ -404,7 +438,7 @@ trait AbstractType extends NonTemplateMemberEntity with HigherKinded { /** An type alias of a template. */ -trait AliasType extends NonTemplateMemberEntity with HigherKinded { +trait AliasType extends MemberTemplateEntity with HigherKinded { /** The type aliased by this type alias. */ def alias: TypeEntity @@ -495,6 +529,9 @@ trait ImplicitConversion { /** The members inherited by this implicit conversion */ def members: List[MemberEntity] + + /** Is this a common implicit conversion (aka conversion that affects all classes, in Predef?) */ + def isCommonConversion: Boolean } /** Shadowing captures the information that the member is shadowed by some other members diff --git a/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala index 6392de22ff..f4c96505a7 100755 --- a/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/IndexModelFactory.scala @@ -36,7 +36,6 @@ object IndexModelFactory { } + d this(firstLetter) = letter + (d.name -> members) } - } //@scala.annotation.tailrec // TODO @@ -46,11 +45,7 @@ object IndexModelFactory { case tpl: DocTemplateEntity => result.addMember(tpl) gather(tpl) - case alias: AliasType => - result.addMember(alias) - case absType: AbstractType => - result.addMember(absType) - case non: NonTemplateMemberEntity if !non.isConstructor => + case non: MemberEntity if !non.isConstructor => result.addMember(non) case x @ _ => } diff --git a/src/compiler/scala/tools/nsc/doc/model/Links.scala b/src/compiler/scala/tools/nsc/doc/model/Links.scala new file mode 100644 index 0000000000..b76dee0f14 --- /dev/null +++ b/src/compiler/scala/tools/nsc/doc/model/Links.scala @@ -0,0 +1,24 @@ +/* NSC -- new Scala compiler + * Copyright 2007-2011 LAMP/EPFL + */ + +package scala.tools.nsc +package doc +package model + +import scala.collection._ + +abstract sealed class LinkTo +case class LinkToTpl(tpl: DocTemplateEntity) extends LinkTo +case class LinkToMember(mbr: MemberEntity, inTpl: DocTemplateEntity) extends LinkTo +case class Tooltip(name: String) extends LinkTo { def this(tpl: TemplateEntity) = this(tpl.qualifiedName) } +// case class LinkToExternal(name: String, url: String) extends LinkTo // for SI-191, whenever Manohar will have time +case object NoLink extends LinkTo // you should use Tooltip if you have a name from the user, this is only in case all fails + +object LinkToTpl { + // this makes it easier to create links + def apply(tpl: TemplateEntity) = tpl match { + case dtpl: DocTemplateEntity => new LinkToTpl(dtpl) + case ntpl: TemplateEntity => new Tooltip(ntpl.qualifiedName) + } +} diff --git a/src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala b/src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala new file mode 100644 index 0000000000..ab14498a7c --- /dev/null +++ b/src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala @@ -0,0 +1,187 @@ +package scala.tools.nsc +package doc +package model + +import comment._ + +import scala.reflect.internal.util.FakePos //Position + +/** This trait extracts all required information for documentation from compilation units */ +trait MemberLookup { + thisFactory: ModelFactory => + + import global._ + + def makeEntityLink(title: Inline, pos: Position, query: String, inTplOpt: Option[DocTemplateImpl]) = + new EntityLink(title) { lazy val link = memberLookup(pos, query, inTplOpt) } + + def memberLookup(pos: Position, query: String, inTplOpt: Option[DocTemplateImpl]): LinkTo = { + assert(modelFinished) + + var members = breakMembers(query) + //println(query + " => " + members) + + // (1) Lookup in the root package, as most of the links are qualified + var linkTo: List[LinkTo] = lookupInRootPackage(pos, members) + + // (2) Recursively go into each + if (inTplOpt.isDefined) { + var currentTpl = inTplOpt.get + while (currentTpl != null && !currentTpl.isRootPackage && (linkTo.isEmpty)) { + linkTo = lookupInTemplate(pos, members, currentTpl) + currentTpl = currentTpl.inTemplate + } + } + + // (3) Look at external links + if (linkTo.isEmpty) { + // TODO: IF THIS IS THE ROOT PACKAGE, LOOK AT EXTERNAL LINKS + } + + // (4) if we still haven't found anything, create a tooltip, if we found too many, report + if (linkTo.isEmpty){ + if (!settings.docNoLinkWarnings.value) + reporter.warning(pos, "Could not find any member to link for \"" + query + "\".") + Tooltip(query) + } else { + if (linkTo.length > 1) { + + val chosen = + if (linkTo.exists(_.isInstanceOf[LinkToMember])) + linkTo.collect({case lm: LinkToMember => lm}).min(Ordering[MemberEntity].on[LinkToMember](_.mbr)) + else + linkTo.head + + def linkToString(link: LinkTo) = { + val description = + link match { + case lm@LinkToMember(mbr, inTpl) => " * " + mbr.kind + " \"" + mbr.signature + "\" in " + inTpl.kind + " " + inTpl.qualifiedName + case lt@LinkToTpl(tpl) => " * " + tpl.kind + " \"" + tpl.qualifiedName + "\"" + case other => " * " + other.toString + } + val chosenInfo = + if (link == chosen) + " [chosen]" + else + "" + description + chosenInfo + "\n" + } + if (!settings.docNoLinkWarnings.value) + reporter.warning(pos, + "The link target \"" + query + "\" is ambiguous. Several (possibly overloaded) members fit the target:\n" + + linkTo.map(link => linkToString(link)).mkString + + (if (MemberLookup.showExplanation) + "\n\n" + + "Quick crash course on using Scaladoc links\n" + + "==========================================\n" + + "Disambiguating terms and types: Prefix terms with '$' and types with '!' in case both names are in use:\n" + + " - [[scala.collection.immutable.List!.apply class List's apply method]] and\n" + + " - [[scala.collection.immutable.List$.apply object List's apply method]]\n" + + "Disambiguating overloaded members: If a term is overloaded, you can indicate the first part of its signature followed by *:\n" + + " - [[[scala.collection.immutable.List$.fill[A](Int)(⇒A):List[A]* Fill with a single parameter]]]\n" + + " - [[[scala.collection.immutable.List$.fill[A](Int,Int)(⇒A):List[List[A]]* Fill with a two parameters]]]\n" + + "Notes: \n" + + " - you can use any number of matching square brackets to avoid interference with the signature\n" + + " - you can use \\. to escape dots in prefixes (don't forget to use * at the end to match the signature!)\n" + + " - you can use \\# to escape hashes, otherwise they will be considered as delimiters, like dots.\n" + else "") + ) + chosen + } else + linkTo.head + } + } + + private abstract class SearchStrategy + private object BothTypeAndTerm extends SearchStrategy + private object OnlyType extends SearchStrategy + private object OnlyTerm extends SearchStrategy + + private def lookupInRootPackage(pos: Position, members: List[String]) = lookupInTemplate(pos, members, makeRootPackage) + + private def lookupInTemplate(pos: Position, members: List[String], inTpl: DocTemplateImpl): List[LinkTo] = { + // Maintaining compatibility with previous links is a bit tricky here: + // we have a preference for term names for all terms except for the last, where we prefer a class: + // How to do this: + // - at each step we do a DFS search with the prefered strategy + // - if the search doesn't return any members, we backtrack on the last decision + // * we look for terms with the last member's name + // * we look for types with the same name, all the way up + val result = members match { + case Nil => + Nil + case mbrName::Nil => + var members = lookupInTemplate(pos, mbrName, inTpl, OnlyType) + if (members.isEmpty) + members = lookupInTemplate(pos, mbrName, inTpl, OnlyTerm) + + members.map(_ match { + case tpl: DocTemplateEntity => LinkToTpl(tpl) + case mbr => LinkToMember(mbr, inTpl) + }) + + case tplName::rest => + + def completeSearch(mbrs: List[MemberImpl]) = + mbrs.collect({case d:DocTemplateImpl => d}).flatMap(tpl => lookupInTemplate(pos, rest, tpl)) + + var members = completeSearch(lookupInTemplate(pos, tplName, inTpl, OnlyTerm)) + if (members.isEmpty) + members = completeSearch(lookupInTemplate(pos, tplName, inTpl, OnlyType)) + + members + } + //println("lookupInTemplate(" + members + ", " + inTpl + ") => " + result) + result + } + + private def lookupInTemplate(pos: Position, member: String, inTpl: DocTemplateImpl, strategy: SearchStrategy): List[MemberImpl] = { + val name = member.stripSuffix("$").stripSuffix("!").stripSuffix("*") + val result = if (member.endsWith("$")) + inTpl.members.filter(mbr => (mbr.name == name) && (mbr.isTerm)) + else if (member.endsWith("!")) + inTpl.members.filter(mbr => (mbr.name == name) && (mbr.isType)) + else if (member.endsWith("*")) + inTpl.members.filter(mbr => (mbr.signature.startsWith(name))) + else { + if (strategy == BothTypeAndTerm) + inTpl.members.filter(_.name == name) + else if (strategy == OnlyType) + inTpl.members.filter(mbr => (mbr.name == name) && (mbr.isType)) + else if (strategy == OnlyTerm) + inTpl.members.filter(mbr => (mbr.name == name) && (mbr.isTerm)) + else + Nil + } + + //println("lookupInTemplate(" + member + ", " + inTpl + ") => " + result) + result + } + + private def breakMembers(query: String): List[String] = { + // Okay, how does this work? Well: you split on . but you don't want to split on \. => thus the ugly regex + // query.split((?<=[^\\\\])\\.).map(_.replaceAll("\\.")) + // The same code, just faster: + var members = List[String]() + var index = 0 + var last_index = 0 + val length = query.length + while (index < length) { + if ((query.charAt(index) == '.' || query.charAt(index) == '#') && + ((index == 0) || (query.charAt(index-1) != '\\'))) { + + members ::= query.substring(last_index, index).replaceAll("\\\\([#\\.])", "$1") + last_index = index + 1 + } + index += 1 + } + if (last_index < length) + members ::= query.substring(last_index, length).replaceAll("\\\\\\.", ".") + members.reverse + } +} + +object MemberLookup { + private[this] var _showExplanation = true + def showExplanation: Boolean = if (_showExplanation) { _showExplanation = false; true } else false +}
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala index 9fa6619e9f..00e6f3769e 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala @@ -19,7 +19,13 @@ import model.{ RootPackage => RootPackageEntity } /** This trait extracts all required information for documentation from compilation units */ class ModelFactory(val global: Global, val settings: doc.Settings) { - thisFactory: ModelFactory with ModelFactoryImplicitSupport with DiagramFactory with CommentFactory with TreeFactory => + thisFactory: ModelFactory + with ModelFactoryImplicitSupport + with ModelFactoryTypeSupport + with DiagramFactory + with CommentFactory + with TreeFactory + with MemberLookup => import global._ import definitions.{ ObjectClass, NothingClass, AnyClass, AnyValClass, AnyRefClass } @@ -32,7 +38,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { private var universe: Universe = null private def dbg(msg: String) = if (sys.props contains "scala.scaladoc.debug") println(msg) - private def closestPackage(sym: Symbol) = { + protected def closestPackage(sym: Symbol) = { if (sym.isPackage || sym.isPackageClass) sym else sym.enclosingPackage } @@ -63,7 +69,10 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { private val droppedPackages = mutable.Set[PackageImpl]() protected val docTemplatesCache = new mutable.LinkedHashMap[Symbol, DocTemplateImpl] protected val noDocTemplatesCache = new mutable.LinkedHashMap[Symbol, NoDocTemplateImpl] - protected var typeCache = new mutable.LinkedHashMap[Type, TypeEntity] + def packageDropped(tpl: DocTemplateImpl) = tpl match { + case p: PackageImpl => droppedPackages(p) + case _ => false + } def optimize(str: String): String = if (str.length < 16) str.intern else str @@ -83,6 +92,9 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def toRoot: List[EntityImpl] = this :: inTpl.toRoot def qualifiedName = name def annotations = sym.annotations.map(makeAnnotation) + def inPackageObject: Boolean = sym.owner.isModuleClass && sym.owner.sourceModule.isPackageObject + def isType = sym.name.isTypeName + def isTerm = sym.name.isTermName } trait TemplateImpl extends EntityImpl with TemplateEntity { @@ -94,13 +106,22 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def isObject = sym.isModule && !sym.isPackage def isCaseClass = sym.isCaseClass def isRootPackage = false - def ownType = makeType(sym.tpe, this) + def isNoDocMemberTemplate = false def selfType = if (sym.thisSym eq sym) None else Some(makeType(sym.thisSym.typeOfThis, this)) - def inPackageObject: Boolean = sym.owner.isModuleClass && sym.owner.sourceModule.isPackageObject } - abstract class MemberImpl(sym: Symbol, implConv: ImplicitConversionImpl, inTpl: DocTemplateImpl) extends EntityImpl(sym, inTpl) with MemberEntity { - lazy val comment = if (inTpl != null) thisFactory.comment(sym, inTpl) else None + abstract class MemberImpl(sym: Symbol, inTpl: DocTemplateImpl) extends EntityImpl(sym, inTpl) with MemberEntity { + lazy val comment = { + // If the current tpl is a DocTemplate, we consider itself as the root for resolving link targets (instead of the + // package the class is in) -- so people can refer to methods directly [[foo]], instead of using [[MyClass.foo]] + // in the doc comment of MyClass + val thisTpl = this match { + case d: DocTemplateImpl => Some(d) + case _ => None + } + if (inTpl != null) thisFactory.comment(sym, thisTpl, inTpl) else None + } + def group = if (comment.isDefined) comment.get.group.getOrElse("No Group") else "No Group" override def inTemplate = inTpl override def toRoot: List[MemberImpl] = this :: inTpl.toRoot def inDefinitionTemplates = this match { @@ -138,16 +159,16 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { * }}} * the type the method returns is TraversableOps, which has all-abstract symbols. But in reality, it couldn't have * any abstract terms, otherwise it would fail compilation. So we reset the DEFERRED flag. */ - if (!sym.isTrait && (sym hasFlag Flags.DEFERRED) && (implConv eq null)) fgs += Paragraph(Text("abstract")) + if (!sym.isTrait && (sym hasFlag Flags.DEFERRED) && (!isImplicitlyInherited)) fgs += Paragraph(Text("abstract")) if (!sym.isModule && (sym hasFlag Flags.FINAL)) fgs += Paragraph(Text("final")) fgs.toList } def deprecation = if (sym.isDeprecated) Some((sym.deprecationMessage, sym.deprecationVersion) match { - case (Some(msg), Some(ver)) => parseWiki("''(Since version " + ver + ")'' " + msg, NoPosition) - case (Some(msg), None) => parseWiki(msg, NoPosition) - case (None, Some(ver)) => parseWiki("''(Since version " + ver + ")''", NoPosition) + case (Some(msg), Some(ver)) => parseWiki("''(Since version " + ver + ")'' " + msg, NoPosition, Some(inTpl)) + case (Some(msg), None) => parseWiki(msg, NoPosition, Some(inTpl)) + case (None, Some(ver)) => parseWiki("''(Since version " + ver + ")''", NoPosition, Some(inTpl)) case (None, None) => Body(Nil) }) else @@ -155,9 +176,9 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def migration = if(sym.hasMigrationAnnotation) Some((sym.migrationMessage, sym.migrationVersion) match { - case (Some(msg), Some(ver)) => parseWiki("''(Changed in version " + ver + ")'' " + msg, NoPosition) - case (Some(msg), None) => parseWiki(msg, NoPosition) - case (None, Some(ver)) => parseWiki("''(Changed in version " + ver + ")''", NoPosition) + case (Some(msg), Some(ver)) => parseWiki("''(Changed in version " + ver + ")'' " + msg, NoPosition, Some(inTpl)) + case (Some(msg), None) => parseWiki(msg, NoPosition, Some(inTpl)) + case (None, Some(ver)) => parseWiki("''(Changed in version " + ver + ")''", NoPosition, Some(inTpl)) case (None, None) => Body(Nil) }) else @@ -172,7 +193,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { case NullaryMethodType(res) => resultTpe(res) case _ => tpe } - val tpe = if (implConv eq null) sym.tpe else implConv.toType memberInfo sym + val tpe = if (!isImplicitlyInherited) sym.tpe else byConversion.get.toType memberInfo sym makeTypeInTemplateContext(resultTpe(tpe), inTemplate, sym) } def isDef = false @@ -184,11 +205,43 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def isAliasType = false def isAbstractType = false def isAbstract = - // for the explanation of implConv == null see comment on flags - ((!sym.isTrait && ((sym hasFlag Flags.ABSTRACT) || (sym hasFlag Flags.DEFERRED)) && (implConv == null)) || + // for the explanation of conversion == null see comment on flags + ((!sym.isTrait && ((sym hasFlag Flags.ABSTRACT) || (sym hasFlag Flags.DEFERRED)) && (!isImplicitlyInherited)) || sym.isAbstractClass || sym.isAbstractType) && !sym.isSynthetic def isTemplate = false - def byConversion = if (implConv ne null) Some(implConv) else None + lazy val signature = { + + def defParams(mbr: Any): String = mbr match { + case d: MemberEntity with Def => + val paramLists: List[String] = + if (d.valueParams.isEmpty) Nil + else d.valueParams map (ps => ps map (_.resultType.name) mkString ("(",",",")")) + paramLists.mkString + case _ => "" + } + + def tParams(mbr: Any): String = mbr match { + case hk: HigherKinded if !hk.typeParams.isEmpty => + def boundsToString(hi: Option[TypeEntity], lo: Option[TypeEntity]): String = { + def bound0(bnd: Option[TypeEntity], pre: String): String = bnd match { + case None => "" + case Some(tpe) => pre ++ tpe.toString + } + bound0(hi, "<:") ++ bound0(lo, ">:") + } + "[" + hk.typeParams.map(tp => tp.variance + tp.name + tParams(tp) + boundsToString(tp.hi, tp.lo)).mkString(", ") + "]" + case _ => "" + } + + (name + tParams(this) + defParams(this) +":"+ resultType.name).replaceAll("\\s","") // no spaces allowed, they break links + } + // these only apply for NonTemplateMemberEntities + def useCaseOf: Option[MemberEntity] = None + def byConversion: Option[ImplicitConversionImpl] = None + def isImplicitlyInherited = false + def isShadowedImplicit = false + def isAmbiguousImplicit = false + def isShadowedOrAmbiguousImplicit = false } /** A template that is not documented at all. The class is instantiated during lookups, to indicate that the class @@ -198,26 +251,53 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { assert(modelFinished) assert(!(noDocTemplatesCache isDefinedAt sym)) noDocTemplatesCache += (sym -> this) - def isDocTemplate = false } /** An inherited template that was not documented in its original owner - example: * in classpath: trait T { class C } -- T (and implicitly C) are not documented - * in the source: trait U extends T -- C appears in U as a NoDocTemplateMemberImpl -- that is, U has a member for it + * in the source: trait U extends T -- C appears in U as a MemberTemplateImpl -- that is, U has a member for it * but C doesn't get its own page */ - class NoDocTemplateMemberImpl(sym: Symbol, inTpl: DocTemplateImpl) extends MemberImpl(sym, null, inTpl) with TemplateImpl with HigherKindedImpl with NoDocTemplateMemberEntity { - assert(modelFinished) - + abstract class MemberTemplateImpl(sym: Symbol, inTpl: DocTemplateImpl) extends MemberImpl(sym, inTpl) with TemplateImpl with HigherKindedImpl with MemberTemplateEntity { + // no templates cache for this class, each owner gets its own instance + override def isTemplate = true def isDocTemplate = false + override def isNoDocMemberTemplate = true lazy val definitionName = optimize(inDefinitionTemplates.head.qualifiedName + "." + name) + def valueParams: List[List[ValueParam]] = Nil /** TODO, these are now only computed for DocTemplates */ + + // Seems unused + // def parentTemplates = + // if (sym.isPackage || sym == AnyClass) + // List() + // else + // sym.tpe.parents.flatMap { tpe: Type => + // val tSym = tpe.typeSymbol + // if (tSym != NoSymbol) + // List(makeTemplate(tSym)) + // else + // List() + // } filter (_.isInstanceOf[DocTemplateEntity]) + + def parentTypes = + if (sym.isPackage || sym == AnyClass) List() else { + val tps = (this match { + case a: AliasType => sym.tpe.dealias.parents + case a: AbstractType => sym.info.bounds match { + case TypeBounds(lo, hi) => List(hi) + case _ => Nil + } + case _ => sym.tpe.parents + }) map { _.asSeenFrom(sym.thisType, sym) } + makeParentTypes(RefinedType(tps, EmptyScope), Some(this), inTpl) + } } /** The instantiation of `TemplateImpl` triggers the creation of the following entities: * All ancestors of the template and all non-package members. */ - abstract class DocTemplateImpl(sym: Symbol, inTpl: DocTemplateImpl) extends MemberImpl(sym, null, inTpl) with TemplateImpl with HigherKindedImpl with DocTemplateEntity { + abstract class DocTemplateImpl(sym: Symbol, inTpl: DocTemplateImpl) extends MemberTemplateImpl(sym, inTpl) with DocTemplateEntity { assert(!modelFinished) assert(!(docTemplatesCache isDefinedAt sym), sym) docTemplatesCache += (sym -> this) @@ -253,24 +333,6 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { else None } - def parentTemplates = - if (sym.isPackage || sym == AnyClass) - List() - else - sym.tpe.parents.flatMap { tpe: Type => - val tSym = tpe.typeSymbol - if (tSym != NoSymbol) - List(makeTemplate(tSym)) - else - List() - } filter (_.isInstanceOf[DocTemplateEntity]) - - def parentTypes = - if (sym.isPackage || sym == AnyClass) List() else { - val tps = sym.tpe.parents map { _.asSeenFrom(sym.thisType, sym) } - makeParentTypes(RefinedType(tps, EmptyScope), Some(this), inTpl) - } - protected def linearizationFromSymbol(symbol: Symbol): List[(TemplateEntity, TypeEntity)] = { symbol.ancestors map { ancestor => val typeEntity = makeType(symbol.info.baseType(ancestor), this) @@ -299,14 +361,14 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def directSubClasses = allSubClasses.filter(_.parentTypes.map(_._1).contains(this)) /* Implcitly convertible class cache */ - private var implicitlyConvertibleClassesCache: mutable.ListBuffer[(DocTemplateEntity, ImplicitConversionImpl)] = null - def registerImplicitlyConvertibleClass(dtpl: DocTemplateEntity, conv: ImplicitConversionImpl): Unit = { + private var implicitlyConvertibleClassesCache: mutable.ListBuffer[(DocTemplateImpl, ImplicitConversionImpl)] = null + def registerImplicitlyConvertibleClass(dtpl: DocTemplateImpl, conv: ImplicitConversionImpl): Unit = { if (implicitlyConvertibleClassesCache == null) - implicitlyConvertibleClassesCache = mutable.ListBuffer[(DocTemplateEntity, ImplicitConversionImpl)]() + implicitlyConvertibleClassesCache = mutable.ListBuffer[(DocTemplateImpl, ImplicitConversionImpl)]() implicitlyConvertibleClassesCache += ((dtpl, conv)) } - def incomingImplicitlyConvertedClasses: List[(DocTemplateEntity, ImplicitConversionImpl)] = + def incomingImplicitlyConvertedClasses: List[(DocTemplateImpl, ImplicitConversionImpl)] = if (implicitlyConvertibleClassesCache == null) List() else @@ -321,16 +383,16 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { lazy val memberSyms = sym.info.members.filter(s => membersShouldDocument(s, this)) // the inherited templates (classes, traits or objects) - var memberSymsLazy = memberSyms.filter(t => templateShouldDocument(t, this) && !inOriginalOnwer(t, this)) + var memberSymsLazy = memberSyms.filter(t => templateShouldDocument(t, this) && !inOriginalOwner(t, this)) // the direct members (methods, values, vars, types and directly contained templates) var memberSymsEager = memberSyms.filter(!memberSymsLazy.contains(_)) // the members generated by the symbols in memberSymsEager - val ownMembers = (memberSyms.flatMap(makeMember(_, null, this))) + val ownMembers = (memberSymsEager.flatMap(makeMember(_, None, this))) // all the members that are documentented PLUS the members inherited by implicit conversions var members: List[MemberImpl] = ownMembers - def templates = members collect { case c: DocTemplateEntity => c } + def templates = members collect { case c: TemplateEntity with MemberEntity => c } def methods = members collect { case d: Def => d } def values = members collect { case v: Val => v } def abstractTypes = members collect { case t: AbstractType => t } @@ -342,13 +404,15 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { */ def completeModel: Unit = { // DFS completion - for (member <- members) - member match { - case d: DocTemplateImpl => d.completeModel - case _ => - } + // since alias types and abstract types have no own members, there's no reason for them to call completeModel + if (!sym.isAliasType && !sym.isAbstractType) + for (member <- members) + member match { + case d: DocTemplateImpl => d.completeModel + case _ => + } - members :::= memberSymsLazy.map(modelCreation.createLazyTemplateMember(_, inTpl)) + members :::= memberSymsLazy.map(modelCreation.createLazyTemplateMember(_, this)) // compute linearization to register subclasses linearization @@ -369,7 +433,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { conv.targetTypeComponents map { case pair@(template, tpe) => template match { - case d: DocTemplateImpl => d.registerImplicitlyConvertibleClass(this, conv) + case d: DocTemplateImpl if (d != this) => d.registerImplicitlyConvertibleClass(this, conv) case _ => // nothing } (pair._1, pair._2, conv) @@ -378,21 +442,66 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { ) override def isTemplate = true - lazy val definitionName = optimize(inDefinitionTemplates.head.qualifiedName + "." + name) - def isDocTemplate = true - def companion = sym.companionSymbol match { - case NoSymbol => None - case comSym if !isEmptyJavaObject(comSym) && (comSym.isClass || comSym.isModule) => - makeTemplate(comSym) match { - case d: DocTemplateImpl => Some(d) - case _ => None + override def isDocTemplate = true + private[this] lazy val companionSymbol = + if (sym.isAliasType || sym.isAbstractType) { + inTpl.sym.info.member(sym.name.toTermName) match { + case NoSymbol => NoSymbol + case s => + s.info match { + case ot: OverloadedType => + NoSymbol + case _ => + // that's to navigate from val Foo: FooExtractor to FooExtractor :) + s.info.resultType.typeSymbol + } } - case _ => None + } + else + sym.companionSymbol + + def companion = + companionSymbol match { + case NoSymbol => None + case comSym if !isEmptyJavaObject(comSym) && (comSym.isClass || comSym.isModule) => + makeTemplate(comSym) match { + case d: DocTemplateImpl => Some(d) + case _ => None + } + case _ => None + } + + def constructors: List[MemberImpl with Constructor] = if (isClass) members collect { case d: Constructor => d } else Nil + def primaryConstructor: Option[MemberImpl with Constructor] = if (isClass) constructors find { _.isPrimary } else None + override def valueParams = + // we don't want params on a class (non case class) signature + if (isCaseClass) primaryConstructor match { + case Some(const) => const.sym.paramss map (_ map (makeValueParam(_, this))) + case None => List() + } + else List.empty + + // These are generated on-demand, make sure you don't call them more than once + def inheritanceDiagram = makeInheritanceDiagram(this) + def contentDiagram = makeContentDiagram(this) + + def groupSearch[T](extractor: Comment => T, default: T): T = { + // query this template + if (comment.isDefined) { + val entity = extractor(comment.get) + if (entity != default) return entity + } + // query linearization + for (tpl <- linearizationTemplates.collect{ case dtpl: DocTemplateImpl if dtpl!=this => dtpl}) { + val entity = tpl.groupSearch(extractor, default) + if (entity != default) return entity + } + default } - // We make the diagram a lazy val, since we're not sure we'll include the diagrams in the page - lazy val inheritanceDiagram = makeInheritanceDiagram(this) - lazy val contentDiagram = makeContentDiagram(this) + def groupDescription(group: String): Option[Body] = groupSearch(_.groupDesc.get(group), None) + def groupPriority(group: String): Int = groupSearch(_.groupPrio.get(group) match { case Some(prio) => prio; case _ => 0 }, 0) + def groupName(group: String): String = groupSearch(_.groupNames.get(group) match { case Some(name) => name; case _ => group }, group) } abstract class PackageImpl(sym: Symbol, inTpl: PackageImpl) extends DocTemplateImpl(sym, inTpl) with Package { @@ -409,18 +518,49 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { abstract class RootPackageImpl(sym: Symbol) extends PackageImpl(sym, null) with RootPackageEntity - abstract class NonTemplateMemberImpl(sym: Symbol, implConv: ImplicitConversionImpl, inTpl: DocTemplateImpl) extends MemberImpl(sym, implConv, inTpl) with NonTemplateMemberEntity { + abstract class NonTemplateMemberImpl(sym: Symbol, conversion: Option[ImplicitConversionImpl], + override val useCaseOf: Option[MemberEntity], inTpl: DocTemplateImpl) + extends MemberImpl(sym, inTpl) with NonTemplateMemberEntity { + override lazy val comment = { + val inRealTpl = + /* Variable precendence order for implicitly added members: Take the variable defifinitions from ... + * 1. the target of the implicit conversion + * 2. the definition template (owner) + * 3. the current template + */ + if (conversion.isDefined) findTemplateMaybe(conversion.get.toType.typeSymbol) match { + case Some(d) if d != makeRootPackage => d //in case of NoSymbol, it will give us the root package + case _ => findTemplateMaybe(sym.owner) match { + case Some(d) if d != makeRootPackage => d //in case of NoSymbol, it will give us the root package + case _ => inTpl + } + } else inTpl + if (inRealTpl != null) thisFactory.comment(sym, None, inRealTpl) else None + } + override def qualifiedName = optimize(inTemplate.qualifiedName + "#" + name) - lazy val definitionName = - if (implConv == null) optimize(inDefinitionTemplates.head.qualifiedName + "#" + name) - else optimize(implConv.conversionQualifiedName + "#" + name) - def isUseCase = sym.isSynthetic + lazy val definitionName = { + // this contrived name is here just to satisfy some older tests -- if you decide to remove it, be my guest, and + // also remove property("package object") from test/scaladoc/scalacheck/HtmlFactoryTest.scala so you don't break + // the test suite... + val packageObject = if (inPackageObject) ".package" else "" + if (!conversion.isDefined) optimize(inDefinitionTemplates.head.qualifiedName + packageObject + "#" + name) + else optimize(conversion.get.conversionQualifiedName + packageObject + "#" + name) + } def isBridge = sym.isBridge + def isUseCase = useCaseOf.isDefined + override def byConversion: Option[ImplicitConversionImpl] = conversion + override def isImplicitlyInherited = { assert(modelFinished); conversion.isDefined } + override def isShadowedImplicit = isImplicitlyInherited && inTpl.implicitsShadowing.get(this).map(_.isShadowed).getOrElse(false) + override def isAmbiguousImplicit = isImplicitlyInherited && inTpl.implicitsShadowing.get(this).map(_.isAmbiguous).getOrElse(false) + override def isShadowedOrAmbiguousImplicit = isShadowedImplicit || isAmbiguousImplicit } - abstract class NonTemplateParamMemberImpl(sym: Symbol, implConv: ImplicitConversionImpl, inTpl: DocTemplateImpl) extends NonTemplateMemberImpl(sym, implConv, inTpl) { + abstract class NonTemplateParamMemberImpl(sym: Symbol, conversion: Option[ImplicitConversionImpl], + useCaseOf: Option[MemberEntity], inTpl: DocTemplateImpl) + extends NonTemplateMemberImpl(sym, conversion, useCaseOf, inTpl) { def valueParams = { - val info = if (implConv eq null) sym.info else implConv.toType memberInfo sym + val info = if (!isImplicitlyInherited) sym.info else conversion.get.toType memberInfo sym info.paramss map { ps => (ps.zipWithIndex) map { case (p, i) => if (p.nameString contains "$") makeValueParam(p, inTpl, optimize("arg" + i)) else makeValueParam(p, inTpl) }} @@ -431,6 +571,12 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { val name = optimize(sym.nameString) } + private trait AliasImpl { + def sym: Symbol + def inTpl: TemplateImpl + def alias = makeTypeInTemplateContext(sym.tpe.dealias, inTpl, sym) + } + private trait TypeBoundsImpl { def sym: Symbol def inTpl: TemplateImpl @@ -454,14 +600,42 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } /* ============== MAKER METHODS ============== */ - /** */ + /** This method makes it easier to work with the different kinds of symbols created by scalac by stripping down the + * package object abstraction and placing members directly in the package. + * + * Here's the explanation of what we do. The code: + * + * package foo { + * object `package` { + * class Bar + * } + * } + * + * will yield this Symbol structure: + * +---------+ (2) + * | | + * +---------------+ +---------- v ------- | ---+ +--------+ (2) + * | package foo#1 <---(1)---- module class foo#2 | | | | + * +---------------+ | +------------------ | -+ | +------------------- v ---+ | + * | | package object foo#3 <-----(1)---- module class package#4 | | + * | +----------------------+ | | +---------------------+ | | + * +--------------------------+ | | class package$Bar#5 | | | + * | +----------------- | -+ | | + * +------------------- | ---+ | + * | | + * +--------+ + * (1) sourceModule + * (2) you get out of owners with .owner + * + * and normalizeTemplate(Bar.owner) will get us the package, instead of the module class of the package object. + */ def normalizeTemplate(aSym: Symbol): Symbol = aSym match { case null | rootMirror.EmptyPackage | NoSymbol => normalizeTemplate(RootPackage) case ObjectClass => normalizeTemplate(AnyRefClass) case _ if aSym.isPackageObject => - aSym + normalizeTemplate(aSym.owner) case _ if aSym.isModuleClass => normalizeTemplate(aSym.sourceModule) case _ => @@ -477,13 +651,16 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def createRootPackage: PackageImpl = docTemplatesCache.get(RootPackage) match { case Some(root: PackageImpl) => root - case _ => modelCreation.createTemplate(RootPackage, null).asInstanceOf[PackageImpl] + case _ => modelCreation.createTemplate(RootPackage, null) match { + case Some(root: PackageImpl) => root + case _ => sys.error("Scaladoc: Unable to create root package!") + } } /** * Create a template, either a package, class, trait or object */ - def createTemplate(aSym: Symbol, inTpl: DocTemplateImpl): DocTemplateImpl = { + def createTemplate(aSym: Symbol, inTpl: DocTemplateImpl): Option[MemberImpl] = { // don't call this after the model finished! assert(!modelFinished) @@ -493,7 +670,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { import Streamable._ Path(settings.docRootContent.value) match { case f : File => { - val rootComment = closing(f.inputStream)(is => parse(slurp(is), "", NoPosition)) + val rootComment = closing(f.inputStream)(is => parse(slurp(is), "", NoPosition, Option(inTpl))) Some(rootComment) } case _ => None @@ -501,27 +678,24 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } def createDocTemplate(bSym: Symbol, inTpl: DocTemplateImpl): DocTemplateImpl = { - if (bSym.isModule || (bSym.isAliasType && bSym.tpe.typeSymbol.isModule)) - new DocTemplateImpl(bSym, inTpl) with Object - else if (bSym.isTrait || (bSym.isAliasType && bSym.tpe.typeSymbol.isTrait)) - new DocTemplateImpl(bSym, inTpl) with Trait - else if (bSym.isClass || (bSym.isAliasType && bSym.tpe.typeSymbol.isClass)) - new DocTemplateImpl(bSym, inTpl) with Class { - def valueParams = - // we don't want params on a class (non case class) signature - if (isCaseClass) List(sym.constrParamAccessors map (makeValueParam(_, this))) - else List.empty - val constructors = - members collect { case d: Constructor => d } - def primaryConstructor = constructors find { _.isPrimary } - } + assert(!modelFinished) // only created BEFORE the model is finished + if (bSym.isAliasType && bSym != AnyRefClass) + new DocTemplateImpl(bSym, inTpl) with AliasImpl with AliasType { override def isAliasType = true } + else if (bSym.isAbstractType) + new DocTemplateImpl(bSym, inTpl) with TypeBoundsImpl with AbstractType { override def isAbstractType = true } + else if (bSym.isModule) + new DocTemplateImpl(bSym, inTpl) with Object {} + else if (bSym.isTrait) + new DocTemplateImpl(bSym, inTpl) with Trait {} + else if (bSym.isClass || bSym == AnyRefClass) + new DocTemplateImpl(bSym, inTpl) with Class {} else - sys.error("'" + bSym + "' isn't a class, trait or object thus cannot be built as a documentable template") + sys.error("'" + bSym + "' isn't a class, trait or object thus cannot be built as a documentable template.") } val bSym = normalizeTemplate(aSym) if (docTemplatesCache isDefinedAt bSym) - return docTemplatesCache(bSym) + return Some(docTemplatesCache(bSym)) /* Three cases of templates: * (1) root package -- special cased for bootstrapping @@ -529,7 +703,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { * (3) class/object/trait */ if (bSym == RootPackage) // (1) - new RootPackageImpl(bSym) { + Some(new RootPackageImpl(bSym) { override lazy val comment = createRootPackageComment override val name = "root" override def inTemplate = this @@ -541,31 +715,52 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { (bSym.info.members ++ EmptyPackage.info.members) filter { s => s != EmptyPackage && s != RootPackage } - } + }) else if (bSym.isPackage) // (2) - inTpl match { - case inPkg: PackageImpl => - val pack = new PackageImpl(bSym, inPkg) {} - if (pack.templates.isEmpty && pack.memberSymsLazy.isEmpty) - droppedPackages += pack - pack - case _ => - sys.error("'" + bSym + "' must be in a package") - } + if (settings.skipPackage(makeQualifiedName(bSym))) + None + else + inTpl match { + case inPkg: PackageImpl => + val pack = new PackageImpl(bSym, inPkg) {} + // Used to check package pruning works: + //println(pack.qualifiedName) + if (pack.templates.filter(_.isDocTemplate).isEmpty && pack.memberSymsLazy.isEmpty) { + droppedPackages += pack + None + } else + Some(pack) + case _ => + sys.error("'" + bSym + "' must be in a package") + } else { // no class inheritance at this point - assert(inOriginalOnwer(bSym, inTpl)) - createDocTemplate(bSym, inTpl) + assert(inOriginalOwner(bSym, inTpl) || bSym.isAbstractType || bSym.isAliasType, bSym + " in " + inTpl) + Some(createDocTemplate(bSym, inTpl)) } } /** * After the model is completed, no more DocTemplateEntities are created. * Therefore any symbol that still appears is: - * - NoDocTemplateMemberEntity (created here) + * - MemberTemplateEntity (created here) * - NoDocTemplateEntity (created in makeTemplate) */ def createLazyTemplateMember(aSym: Symbol, inTpl: DocTemplateImpl): MemberImpl = { + + // Code is duplicate because the anonymous classes are created statically + def createNoDocMemberTemplate(bSym: Symbol, inTpl: DocTemplateImpl): MemberTemplateImpl = { + assert(modelFinished) // only created AFTER the model is finished + if (bSym.isModule || (bSym.isAliasType && bSym.tpe.typeSymbol.isModule)) + new MemberTemplateImpl(bSym, inTpl) with Object {} + else if (bSym.isTrait || (bSym.isAliasType && bSym.tpe.typeSymbol.isTrait)) + new MemberTemplateImpl(bSym, inTpl) with Trait {} + else if (bSym.isClass || (bSym.isAliasType && bSym.tpe.typeSymbol.isClass)) + new MemberTemplateImpl(bSym, inTpl) with Class {} + else + sys.error("'" + bSym + "' isn't a class, trait or object thus cannot be built as a member template.") + } + assert(modelFinished) val bSym = normalizeTemplate(aSym) @@ -579,7 +774,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { mbrs.head case _ => // move the class completely to the new location - new NoDocTemplateMemberImpl(aSym, inTpl) + createNoDocMemberTemplate(bSym, inTpl) } } } @@ -588,20 +783,18 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def makeRootPackage: PackageImpl = docTemplatesCache(RootPackage).asInstanceOf[PackageImpl] // TODO: Should be able to override the type - def makeMember(aSym: Symbol, implConv: ImplicitConversionImpl, inTpl: DocTemplateImpl): List[MemberImpl] = { + def makeMember(aSym: Symbol, conversion: Option[ImplicitConversionImpl], inTpl: DocTemplateImpl): List[MemberImpl] = { - def makeMember0(bSym: Symbol, _useCaseOf: Option[MemberImpl]): Option[MemberImpl] = { + def makeMember0(bSym: Symbol, useCaseOf: Option[MemberImpl]): Option[MemberImpl] = { if (bSym.isGetter && bSym.isLazy) - Some(new NonTemplateMemberImpl(bSym, implConv, inTpl) with Val { + Some(new NonTemplateMemberImpl(bSym, conversion, useCaseOf, inTpl) with Val { override lazy val comment = // The analyser does not duplicate the lazy val's DocDef when it introduces its accessor. - thisFactory.comment(bSym.accessed, inTpl.asInstanceOf[DocTemplateImpl]) // This hack should be removed after analyser is fixed. + thisFactory.comment(bSym.accessed, None, inTpl.asInstanceOf[DocTemplateImpl]) // This hack should be removed after analyser is fixed. override def isLazyVal = true - override def useCaseOf = _useCaseOf }) else if (bSym.isGetter && bSym.accessed.isMutable) - Some(new NonTemplateMemberImpl(bSym, implConv, inTpl) with Val { + Some(new NonTemplateMemberImpl(bSym, conversion, useCaseOf, inTpl) with Val { override def isVar = true - override def useCaseOf = _useCaseOf }) else if (bSym.isMethod && !bSym.hasAccessorFlag && !bSym.isConstructor && !bSym.isModule) { val cSym = { // This unsightly hack closes issue #4086. @@ -615,45 +808,32 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } else bSym } - Some(new NonTemplateParamMemberImpl(cSym, implConv, inTpl) with HigherKindedImpl with Def { + Some(new NonTemplateParamMemberImpl(cSym, conversion, useCaseOf, inTpl) with HigherKindedImpl with Def { override def isDef = true - override def useCaseOf = _useCaseOf }) } - else if (bSym.isConstructor && (implConv == null)) - Some(new NonTemplateParamMemberImpl(bSym, implConv, inTpl) with Constructor { - override def isConstructor = true - def isPrimary = sym.isPrimaryConstructor - override def useCaseOf = _useCaseOf - }) + else if (bSym.isConstructor) + if (conversion.isDefined) + None // don't list constructors inherted by implicit conversion + else + Some(new NonTemplateParamMemberImpl(bSym, conversion, useCaseOf, inTpl) with Constructor { + override def isConstructor = true + def isPrimary = sym.isPrimaryConstructor + }) else if (bSym.isGetter) // Scala field accessor or Java field - Some(new NonTemplateMemberImpl(bSym, implConv, inTpl) with Val { + Some(new NonTemplateMemberImpl(bSym, conversion, useCaseOf, inTpl) with Val { override def isVal = true - override def useCaseOf = _useCaseOf }) - else if (bSym.isAbstractType) - Some(new NonTemplateMemberImpl(bSym, implConv, inTpl) with TypeBoundsImpl with HigherKindedImpl with AbstractType { + else if (bSym.isAbstractType && !typeShouldDocument(bSym, inTpl)) + Some(new MemberTemplateImpl(bSym, inTpl) with TypeBoundsImpl with AbstractType { override def isAbstractType = true - override def useCaseOf = _useCaseOf }) - else if (bSym.isAliasType && bSym != AnyRefClass) - Some(new NonTemplateMemberImpl(bSym, implConv, inTpl) with HigherKindedImpl with AliasType { + else if (bSym.isAliasType && !typeShouldDocument(bSym, inTpl)) + Some(new MemberTemplateImpl(bSym, inTpl) with AliasImpl with AliasType { override def isAliasType = true - def alias = makeTypeInTemplateContext(sym.tpe.dealias, inTpl, sym) - override def useCaseOf = _useCaseOf }) - else if (bSym.isPackage && !modelFinished) - inTpl match { - case inPkg: PackageImpl => modelCreation.createTemplate(bSym, inTpl) match { - case p: PackageImpl if droppedPackages contains p => None - case p: PackageImpl => Some(p) - case _ => sys.error("'" + bSym + "' must be a package") - } - case _ => - sys.error("'" + bSym + "' must be in a package") - } - else if (!modelFinished && templateShouldDocument(bSym, inTpl) && inOriginalOnwer(bSym, inTpl)) - Some(modelCreation.createTemplate(bSym, inTpl)) + else if (!modelFinished && (bSym.isPackage || bSym.isAliasType || bSym.isAbstractType || templateShouldDocument(bSym, inTpl))) + modelCreation.createTemplate(bSym, inTpl) else None } @@ -680,17 +860,20 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { inTpl.members.find(_.sym == aSym) } + @deprecated("2.10", "Use findLinkTarget instead!") def findTemplate(query: String): Option[DocTemplateImpl] = { assert(modelFinished) - docTemplatesCache.values find { (tpl: TemplateImpl) => tpl.qualifiedName == query && !tpl.isObject } + docTemplatesCache.values find { (tpl: DocTemplateImpl) => tpl.qualifiedName == query && !packageDropped(tpl) && !tpl.isObject } } def findTemplateMaybe(aSym: Symbol): Option[DocTemplateImpl] = { assert(modelFinished) - docTemplatesCache.get(normalizeTemplate(aSym)) + docTemplatesCache.get(normalizeTemplate(aSym)).filterNot(packageDropped(_)) } - def makeTemplate(aSym: Symbol): TemplateImpl = { + def makeTemplate(aSym: Symbol): TemplateImpl = makeTemplate(aSym, None) + + def makeTemplate(aSym: Symbol, inTpl: Option[TemplateImpl]): TemplateImpl = { assert(modelFinished) def makeNoDocTemplate(aSym: Symbol, inTpl: TemplateImpl): NoDocTemplateImpl = { @@ -706,11 +889,10 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { dtpl case None => val bSym = normalizeTemplate(aSym) - makeNoDocTemplate(bSym, makeTemplate(bSym.owner)) + makeNoDocTemplate(bSym, if (inTpl.isDefined) inTpl.get else makeTemplate(bSym.owner)) } } - /** */ def makeAnnotation(annot: AnnotationInfo): Annotation = { val aSym = annot.symbol @@ -720,7 +902,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { val arguments = { // lazy def noParams = annot.args map { _ => None } val params: List[Option[ValueParam]] = annotationClass match { - case aClass: Class => + case aClass: DocTemplateEntity with Class => (aClass.primaryConstructor map { _.valueParams.head }) match { case Some(vps) => vps map { Some(_) } case None => noParams @@ -795,17 +977,37 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } /** Get the types of the parents of the current class, ignoring the refinements */ - def makeParentTypes(aType: Type, tpl: Option[DocTemplateImpl], inTpl: TemplateImpl): List[(TemplateEntity, TypeEntity)] = aType match { + def makeParentTypes(aType: Type, tpl: Option[MemberTemplateImpl], inTpl: TemplateImpl): List[(TemplateEntity, TypeEntity)] = aType match { case RefinedType(parents, defs) => - val ignoreParents = Set[Symbol](AnyRefClass, ObjectClass) + val ignoreParents = Set[Symbol](AnyClass, AnyRefClass, ObjectClass) val filtParents = // we don't want to expose too many links to AnyRef, that will just be redundant information - if (tpl.isDefined && (!tpl.get.isObject && parents.length < 2)) + if (tpl.isDefined && { val sym = tpl.get.sym; (!sym.isModule && parents.length < 2) || (sym == AnyValClass) || (sym == AnyRefClass) || (sym == AnyClass) }) parents else parents.filterNot((p: Type) => ignoreParents(p.typeSymbol)) + + /** Returns: + * - a DocTemplate if the type's symbol is documented + * - a NoDocTemplateMember if the type's symbol is not documented in its parent but in another template + * - a NoDocTemplate if the type's symbol is not documented at all */ + def makeTemplateOrMemberTemplate(parent: Type): TemplateImpl = { + def noDocTemplate = makeTemplate(parent.typeSymbol) + findTemplateMaybe(parent.typeSymbol) match { + case Some(tpl) => tpl + case None => parent match { + case TypeRef(pre, sym, args) => + findTemplateMaybe(pre.typeSymbol) match { + case Some(tpl) => findMember(parent.typeSymbol, tpl).collect({case t: TemplateImpl => t}).getOrElse(noDocTemplate) + case None => noDocTemplate + } + case _ => noDocTemplate + } + } + } + filtParents.map(parent => { - val templateEntity = makeTemplate(parent.typeSymbol) + val templateEntity = makeTemplateOrMemberTemplate(parent) val typeEntity = makeType(parent, inTpl) (templateEntity, typeEntity) }) @@ -813,160 +1015,38 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { List((makeTemplate(aType.typeSymbol), makeType(aType, inTpl))) } - /** */ - def makeType(aType: Type, inTpl: TemplateImpl): TypeEntity = { - def templatePackage = closestPackage(inTpl.sym) - - def createTypeEntity = new TypeEntity { - private val nameBuffer = new StringBuilder - private var refBuffer = new immutable.TreeMap[Int, (TemplateEntity, Int)] - private def appendTypes0(types: List[Type], sep: String): Unit = types match { - case Nil => - case tp :: Nil => - appendType0(tp) - case tp :: tps => - appendType0(tp) - nameBuffer append sep - appendTypes0(tps, sep) - } - - private def appendType0(tpe: Type): Unit = tpe match { - /* Type refs */ - case tp: TypeRef if definitions.isFunctionType(tp) => - val args = tp.normalize.typeArgs - nameBuffer append '(' - appendTypes0(args.init, ", ") - nameBuffer append ") ⇒ " - appendType0(args.last) - case tp: TypeRef if definitions.isScalaRepeatedParamType(tp) => - appendType0(tp.args.head) - nameBuffer append '*' - case tp: TypeRef if definitions.isByNameParamType(tp) => - nameBuffer append "⇒ " - appendType0(tp.args.head) - case tp: TypeRef if definitions.isTupleType(tp) => - val args = tp.normalize.typeArgs - nameBuffer append '(' - appendTypes0(args, ", ") - nameBuffer append ')' - case TypeRef(pre, aSym, targs) => - val preSym = pre.widen.typeSymbol - // There's a work in progress here trying to deal with the - // places where undesirable prefixes are printed. - // ... - // If the prefix is something worthy of printing, see if the prefix type - // is in the same package as the enclosing template. If so, print it - // unqualified and they'll figure it out. - // - // val stripPrefixes = List(templatePackage.fullName + ".", "package.", "java.lang.") - // if (!preSym.printWithoutPrefix) { - // nameBuffer append stripPrefixes.foldLeft(pre.prefixString)(_ stripPrefix _) - // } - val bSym = normalizeTemplate(aSym) - if (bSym.isNonClassType && bSym != AnyRefClass) { - nameBuffer append bSym.decodedName - } else { - val tpl = makeTemplate(bSym) - val pos0 = nameBuffer.length - refBuffer += pos0 -> (tpl, tpl.name.length) - nameBuffer append tpl.name - } - if (!targs.isEmpty) { - nameBuffer append '[' - appendTypes0(targs, ", ") - nameBuffer append ']' - } - /* Refined types */ - case RefinedType(parents, defs) => - val ignoreParents = Set[Symbol](AnyClass, ObjectClass) - val filtParents = parents filterNot (x => ignoreParents(x.typeSymbol)) match { - case Nil => parents - case ps => ps - } - appendTypes0(filtParents, " with ") - // XXX Still todo: properly printing refinements. - // Since I didn't know how to go about displaying a multi-line type, I went with - // printing single method refinements (which should be the most common) and printing - // the number of members if there are more. - defs.toList match { - case Nil => () - case x :: Nil => nameBuffer append (" { " + x.defString + " }") - case xs => nameBuffer append (" { ... /* %d definitions in type refinement */ }" format xs.size) - } - /* Eval-by-name types */ - case NullaryMethodType(result) => - nameBuffer append '⇒' - appendType0(result) - /* Polymorphic types */ - case PolyType(tparams, result) => assert(tparams.nonEmpty) -// throw new Error("Polymorphic type '" + tpe + "' cannot be printed as a type") - def typeParamsToString(tps: List[Symbol]): String = if (tps.isEmpty) "" else - tps.map{tparam => - tparam.varianceString + tparam.name + typeParamsToString(tparam.typeParams) - }.mkString("[", ", ", "]") - nameBuffer append typeParamsToString(tparams) - appendType0(result) - case tpen => - nameBuffer append tpen.toString + def makeQualifiedName(sym: Symbol, relativeTo: Option[Symbol] = None): String = { + val stop = if (relativeTo.isDefined) relativeTo.get.ownerChain.toSet else Set[Symbol]() + var sym1 = sym + var path = new StringBuilder() + // var path = List[Symbol]() + + while ((sym1 != NoSymbol) && (path.isEmpty || !stop(sym1))) { + val sym1Norm = normalizeTemplate(sym1) + if (!sym1.sourceModule.isPackageObject && sym1Norm != RootPackage) { + if (path.length != 0) + path.insert(0, ".") + path.insert(0, sym1Norm.nameString) + // path::= sym1Norm } - appendType0(aType) - val refEntity = refBuffer - val name = optimize(nameBuffer.toString) + sym1 = sym1.owner } - if (aType.isTrivial) - typeCache.get(aType) match { - case Some(typeEntity) => typeEntity - case None => - val typeEntity = createTypeEntity - typeCache += aType -> typeEntity - typeEntity - } - else - createTypeEntity + optimize(path.toString) + //path.mkString(".") } - def normalizeOwner(aSym: Symbol): Symbol = - /* - * Okay, here's the explanation of what happens. The code: - * - * package foo { - * object `package` { - * class Bar - * } - * } - * - * will yield this Symbol structure: - * - * +---------------+ +--------------------------+ - * | package foo#1 ----(1)---> module class foo#2 | - * +---------------+ | +----------------------+ | +-------------------------+ - * | | package object foo#3 ------(1)---> module class package#4 | - * | +----------------------+ | | +---------------------+ | - * +--------------------------+ | | class package$Bar#5 | | - * | +---------------------+ | - * +-------------------------+ - * (1) sourceModule - * (2) you get out of owners with .owner - */ - normalizeTemplate(aSym) match { - case bSym if bSym.isPackageObject => - normalizeOwner(bSym.owner) - case bSym => - bSym - } - - def inOriginalOnwer(aSym: Symbol, inTpl: TemplateImpl): Boolean = - normalizeOwner(aSym.owner) == normalizeOwner(inTpl.sym) + def inOriginalOwner(aSym: Symbol, inTpl: TemplateImpl): Boolean = + normalizeTemplate(aSym.owner) == normalizeTemplate(inTpl.sym) def templateShouldDocument(aSym: Symbol, inTpl: TemplateImpl): Boolean = - (aSym.isClass || aSym.isModule || aSym == AnyRefClass) && + (aSym.isTrait || aSym.isClass || aSym.isModule) && localShouldDocument(aSym) && !isEmptyJavaObject(aSym) && // either it's inside the original owner or we can document it later: - (!inOriginalOnwer(aSym, inTpl) || (aSym.isPackageClass || (aSym.sourceFile != null))) + (!inOriginalOwner(aSym, inTpl) || (aSym.isPackageClass || (aSym.sourceFile != null))) - def membersShouldDocument(sym: Symbol, inTpl: TemplateImpl) = + def membersShouldDocument(sym: Symbol, inTpl: TemplateImpl) = { // pruning modules that shouldn't be documented // Why Symbol.isInitialized? Well, because we need to avoid exploring all the space available to scaladoc // from the classpath -- scaladoc is a hog, it will explore everything starting from the root package unless we @@ -979,6 +1059,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { (!sym.isConstructor || sym.owner == inTpl.sym) && // If the @bridge annotation overrides a normal member, show it !isPureBridge(sym) + } def isEmptyJavaObject(aSym: Symbol): Boolean = aSym.isModule && aSym.isJavaDefined && @@ -995,5 +1076,11 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { // the implicit conversions that are excluded from the pages should not appear in the diagram def implicitExcluded(convertorMethod: String): Boolean = settings.hardcoded.commonConversionTargets.contains(convertorMethod) + + // whether or not to create a page for an {abstract,alias} type + def typeShouldDocument(bSym: Symbol, inTpl: DocTemplateImpl) = + (settings.docExpandAllTypes.value && (bSym.sourceFile != null)) || + { val rawComment = global.expandedDocComment(bSym, inTpl.sym) + rawComment.contains("@template") || rawComment.contains("@documentable") } } diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala index 8cbf2ac1b6..a12b67c9ed 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala @@ -53,7 +53,7 @@ import model.{ RootPackage => RootPackageEntity } * TODO: Give an overview here */ trait ModelFactoryImplicitSupport { - thisFactory: ModelFactory with CommentFactory with TreeFactory => + thisFactory: ModelFactory with ModelFactoryTypeSupport with CommentFactory with TreeFactory => import global._ import global.analyzer._ @@ -115,7 +115,7 @@ trait ModelFactoryImplicitSupport { // Put the class-specific conversions in front val (ownConversions, commonConversions) = - conversions.partition(conv => !hardcoded.commonConversionTargets.contains(conv.conversionQualifiedName)) + conversions.partition(!_.isCommonConversion) ownConversions ::: commonConversions } @@ -242,7 +242,7 @@ trait ModelFactoryImplicitSupport { available = Some(search.tree != EmptyTree) } catch { - case _ => + case _: TypeError => } } @@ -329,11 +329,6 @@ trait ModelFactoryImplicitSupport { } } - def makeQualifiedName(sym: Symbol): String = { - val remove = Set[Symbol](RootPackage, RootClass, EmptyPackage, EmptyPackageClass) - sym.ownerChain.filterNot(remove.contains(_)).reverse.map(_.nameString).mkString(".") - } - /* ============== IMPLEMENTATION PROVIDING ENTITY TYPES ============== */ class ImplicitConversionImpl( @@ -352,7 +347,7 @@ trait ModelFactoryImplicitSupport { if (convSym != NoSymbol) makeTemplate(convSym.owner) else { - error("Scaladoc implicits: Implicit conversion from " + sym.tpe + " to " + toType + " done by " + convSym + " = NoSymbol!") + error("Scaladoc implicits: " + toString + " = NoSymbol!") makeRootPackage } @@ -408,29 +403,22 @@ trait ModelFactoryImplicitSupport { debug("") memberSyms.flatMap({ aSym => - makeTemplate(aSym.owner) match { - case d: DocTemplateImpl => - // we can't just pick up nodes from the previous template, although that would be very convenient: - // they need the byConversion field to be attached to themselves -- this is design decision I should - // revisit soon - // - // d.ownMembers.collect({ - // // it's either a member or has a couple of usecases it's hidden behind - // case m: MemberImpl if m.sym == aSym => - // m // the member itself - // case m: MemberImpl if m.useCaseOf.isDefined && m.useCaseOf.get.asInstanceOf[MemberImpl].sym == aSym => - // m.useCaseOf.get.asInstanceOf[MemberImpl] // the usecase - // }) - makeMember(aSym, this, d) - case _ => - // should only happen if the code for this template is not part of the scaladoc run => - // members won't have any comments - makeMember(aSym, this, inTpl) - } + // we can't just pick up nodes from the original template, although that would be very convenient: + // they need the byConversion field to be attached to themselves and the types to be transformed by + // asSeenFrom + + // at the same time, the member itself is in the inTpl, not in the new template -- but should pick up + // variables from the old template. Ugly huh? We'll always create the member inTpl, but it will change + // the template when expanding variables in the comment :) + makeMember(aSym, Some(this), inTpl) }) } lazy val members: List[MemberEntity] = memberImpls + + def isCommonConversion = hardcoded.commonConversionTargets.contains(conversionQualifiedName) + + override def toString = "Implcit conversion from " + sym.tpe + " to " + toType + " done by " + convSym } /* ========================= HELPER METHODS ========================== */ diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala new file mode 100644 index 0000000000..d2a26d1309 --- /dev/null +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala @@ -0,0 +1,323 @@ +/* NSC -- new Scala compiler -- Copyright 2007-2011 LAMP/EPFL */ + +package scala.tools.nsc +package doc +package model + +import comment._ + +import diagram._ + +import scala.collection._ +import scala.util.matching.Regex + +import symtab.Flags + +import io._ + +import model.{ RootPackage => RootPackageEntity } + +/** This trait extracts all required information for documentation from compilation units */ +trait ModelFactoryTypeSupport { + thisFactory: ModelFactory + with ModelFactoryImplicitSupport + with ModelFactoryTypeSupport + with DiagramFactory + with CommentFactory + with TreeFactory => + + import global._ + import definitions.{ ObjectClass, NothingClass, AnyClass, AnyValClass, AnyRefClass } + import rootMirror.{ RootPackage, RootClass, EmptyPackage } + + protected var typeCache = new mutable.LinkedHashMap[Type, TypeEntity] + + /** */ + def makeType(aType: Type, inTpl: TemplateImpl): TypeEntity = { + def templatePackage = closestPackage(inTpl.sym) + + def createTypeEntity = new TypeEntity { + private var nameBuffer = new StringBuilder + private var refBuffer = new immutable.TreeMap[Int, (LinkTo, Int)] + private def appendTypes0(types: List[Type], sep: String): Unit = types match { + case Nil => + case tp :: Nil => + appendType0(tp) + case tp :: tps => + appendType0(tp) + nameBuffer append sep + appendTypes0(tps, sep) + } + + private def appendType0(tpe: Type): Unit = tpe match { + /* Type refs */ + case tp: TypeRef if definitions.isFunctionType(tp) => + val args = tp.normalize.typeArgs + nameBuffer append '(' + appendTypes0(args.init, ", ") + nameBuffer append ") ⇒ " + appendType0(args.last) + case tp: TypeRef if definitions.isScalaRepeatedParamType(tp) => + appendType0(tp.args.head) + nameBuffer append '*' + case tp: TypeRef if definitions.isByNameParamType(tp) => + nameBuffer append "⇒ " + appendType0(tp.args.head) + case tp: TypeRef if definitions.isTupleType(tp) => + val args = tp.normalize.typeArgs + nameBuffer append '(' + appendTypes0(args, ", ") + nameBuffer append ')' + case TypeRef(pre, aSym, targs) => + val preSym = pre.widen.typeSymbol + + // SI-3314/SI-4888: Classes, Traits and Types can be inherited from a template to another: + // class Enum { abstract class Value } + // class Day extends Enum { object Mon extends Value /*...*/ } + // ===> in such cases we have two options: + // (0) if there's no inheritance taking place (Enum#Value) we can link to the template directly + // (1) if we generate the doc template for Day, we can link to the correct member + // (2) if we don't generate the doc template, we should at least indicate the correct prefix in the tooltip + val bSym = normalizeTemplate(aSym) + val owner = + if ((preSym != NoSymbol) && /* it needs a prefix */ + (preSym != bSym.owner) && /* prefix is different from owner */ + (aSym == bSym)) /* normalization doesn't play tricks on us */ + preSym + else + bSym.owner + + val bTpl = findTemplateMaybe(bSym) + val link = + if (owner == bSym.owner && bTpl.isDefined) + // (0) the owner's class is linked AND has a template - lovely + LinkToTpl(bTpl.get) + else { + val oTpl = findTemplateMaybe(owner) + val bMbr = oTpl.map(findMember(bSym, _)) + if (oTpl.isDefined && bMbr.isDefined && bMbr.get.isDefined) + // (1) the owner's class + LinkToMember(bMbr.get.get, oTpl.get) //ugh + else + // (2) if we still couldn't find the owner, show a tooltip with the qualified name + Tooltip(makeQualifiedName(bSym)) + } + + // SI-4360 Showing prefixes when necessary + // We check whether there's any directly accessible type with the same name in the current template OR if the + // type is inherited from one template to another. There may be multiple symbols with the same name in scope, + // but we won't show the prefix if our symbol is among them, only if *it's not* -- that's equal to showing + // the prefix only for ambiguous references, not for overloaded ones. + def needsPrefix: Boolean = { + if ((owner != bSym.owner || preSym.isRefinementClass) && (normalizeTemplate(owner) != inTpl.sym)) + return true + // don't get tricked into prefixng method type params and existentials: + // I tried several tricks BUT adding the method for which I'm creating the type => that simply won't scale, + // as ValueParams are independent of their parent member, and I really don't want to add this information to + // all terms, as we're already over the allowed memory footprint + if (aSym.isTypeParameterOrSkolem || aSym.isExistentiallyBound /* existential or existential skolem */) + return false + + for (tpl <- inTpl.sym.ownerChain) { + tpl.info.member(bSym.name) match { + case NoSymbol => + // No syms with that name, look further inside the owner chain + case sym => + // Symbol found -- either the correct symbol, another one OR an overloaded alternative + if (sym == bSym) + return false + else sym.info match { + case OverloadedType(owner, alternatives) => + return alternatives.contains(bSym) + case _ => + return true + } + } + } + // if it's not found in the owner chain, we can safely leave out the prefix + false + } + + val prefix = + if (!settings.docNoPrefixes.value && needsPrefix && (bSym != AnyRefClass /* which we normalize */)) { + if (!owner.isRefinementClass) { + val qName = makeQualifiedName(owner, Some(inTpl.sym)) + if (qName != "") qName + "." else "" + } + else { + nameBuffer append "(" + appendType0(pre) + nameBuffer append ")#" + "" // we already appended the prefix + } + } else "" + + //DEBUGGING: + //if (makeQualifiedName(bSym) == "pack1.A") println("needsPrefix(" + bSym + ", " + owner + ", " + inTpl.qualifiedName + ") => " + needsPrefix + " and prefix=" + prefix) + + val name = prefix + bSym.nameString + val pos0 = nameBuffer.length + refBuffer += pos0 -> ((link, name.length)) + nameBuffer append name + + if (!targs.isEmpty) { + nameBuffer append '[' + appendTypes0(targs, ", ") + nameBuffer append ']' + } + /* Refined types */ + case RefinedType(parents, defs) => + val ignoreParents = Set[Symbol](AnyClass, ObjectClass) + val filtParents = parents filterNot (x => ignoreParents(x.typeSymbol)) match { + case Nil => parents + case ps => ps + } + appendTypes0(filtParents, " with ") + // XXX Still todo: properly printing refinements. + // Since I didn't know how to go about displaying a multi-line type, I went with + // printing single method refinements (which should be the most common) and printing + // the number of members if there are more. + defs.toList match { + case Nil => () + case x :: Nil => nameBuffer append (" { " + x.defString + " }") + case xs => nameBuffer append (" { ... /* %d definitions in type refinement */ }" format xs.size) + } + /* Eval-by-name types */ + case NullaryMethodType(result) => + nameBuffer append '⇒' + appendType0(result) + + /* Polymorphic types */ + case PolyType(tparams, result) => assert(tparams.nonEmpty) + def typeParamsToString(tps: List[Symbol]): String = if (tps.isEmpty) "" else + tps.map{tparam => + tparam.varianceString + tparam.name + typeParamsToString(tparam.typeParams) + }.mkString("[", ", ", "]") + nameBuffer append typeParamsToString(tparams) + appendType0(result) + + case et@ExistentialType(quantified, underlying) => + + def appendInfoStringReduced(sym: Symbol, tp: Type): Unit = { + if (sym.isType && !sym.isAliasType && !sym.isClass) { + tp match { + case PolyType(tparams, _) => + nameBuffer append "[" + appendTypes0(tparams.map(_.tpe), ", ") + nameBuffer append "]" + case _ => + } + tp.resultType match { + case rt @ TypeBounds(_, _) => + appendType0(rt) + case rt => + nameBuffer append " <: " + appendType0(rt) + } + } else { + // fallback to the Symbol infoString + nameBuffer append sym.infoString(tp) + } + } + + def appendClauses = { + nameBuffer append " forSome {" + var first = true + val qset = quantified.toSet + for (sym <- quantified) { + if (!first) { nameBuffer append ", " } else first = false + if (sym.isSingletonExistential) { + nameBuffer append "val " + nameBuffer append tpnme.dropSingletonName(sym.name) + nameBuffer append ": " + appendType0(dropSingletonType(sym.info.bounds.hi)) + } else { + if (sym.flagString != "") nameBuffer append (sym.flagString + " ") + if (sym.keyString != "") nameBuffer append (sym.keyString + " ") + nameBuffer append sym.varianceString + nameBuffer append sym.nameString + appendInfoStringReduced(sym, sym.info) + } + } + nameBuffer append "}" + } + + underlying match { + case TypeRef(pre, sym, args) if et.isRepresentableWithWildcards => + appendType0(typeRef(pre, sym, Nil)) + nameBuffer append "[" + var first = true + val qset = quantified.toSet + for (arg <- args) { + if (!first) { nameBuffer append ", " } else first = false + arg match { + case TypeRef(_, sym, _) if (qset contains sym) => + nameBuffer append "_" + appendInfoStringReduced(sym, sym.info) + case arg => + appendType0(arg) + } + } + nameBuffer append "]" + case MethodType(_, _) | NullaryMethodType(_) | PolyType(_, _) => + nameBuffer append "(" + appendType0(underlying) + nameBuffer append ")" + appendClauses + case _ => + appendType0(underlying) + appendClauses + } + + case tb@TypeBounds(lo, hi) => + if (tb.lo != TypeBounds.empty.lo) { + nameBuffer append " >: " + appendType0(lo) + } + if (tb.hi != TypeBounds.empty.hi) { + nameBuffer append " <: " + appendType0(hi) + } + // case tpen: ThisType | SingleType | SuperType => + // if (tpen.isInstanceOf[ThisType] && tpen.asInstanceOf[ThisType].sym.isEffectiveRoot) { + // appendType0 typeRef(NoPrefix, sym, Nil) + // } else { + // val underlying = + // val pre = underlying.typeSymbol.skipPackageObject + // if (pre.isOmittablePrefix) pre.fullName + ".type" + // else prefixString + "type" + case tpen@ThisType(sym) => + appendType0(typeRef(NoPrefix, sym, Nil)) + nameBuffer append ".this" + if (!tpen.underlying.typeSymbol.skipPackageObject.isOmittablePrefix) nameBuffer append ".type" + case tpen@SuperType(thistpe, supertpe) => + nameBuffer append "super[" + appendType0(supertpe) + nameBuffer append "]" + case tpen@SingleType(pre, sym) => + appendType0(typeRef(pre, sym, Nil)) + if (!tpen.underlying.typeSymbol.skipPackageObject.isOmittablePrefix) nameBuffer append ".type" + case tpen => + nameBuffer append tpen.toString + } + appendType0(aType) + val refEntity = refBuffer + val name = optimize(nameBuffer.toString) + nameBuffer = null + } + + // SI-4360: Entity caching depends on both the type AND the template it's in, as the prefixes might change for the + // same type based on the template the type is shown in. + if (settings.docNoPrefixes.value) { + val cached = typeCache.get(aType) + cached match { + case Some(typeEntity) => + typeEntity + case None => + val typeEntity = createTypeEntity + typeCache += aType -> typeEntity + typeEntity + } + } else createTypeEntity + } +}
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/doc/model/TypeEntity.scala b/src/compiler/scala/tools/nsc/doc/model/TypeEntity.scala index 67e955f613..643067cca5 100644 --- a/src/compiler/scala/tools/nsc/doc/model/TypeEntity.scala +++ b/src/compiler/scala/tools/nsc/doc/model/TypeEntity.scala @@ -9,7 +9,6 @@ package model import scala.collection._ - /** A type. Note that types and templates contain the same information only for the simplest types. For example, a type * defines how a template's type parameters are instantiated (as in `List[Cow]`), what the template's prefix is * (as in `johnsFarm.Cow`), and supports compound or structural types. */ @@ -21,9 +20,8 @@ abstract class TypeEntity { /** Maps which parts of this type's name reference entities. The map is indexed by the position of the first * character that reference some entity, and contains the entity and the position of the last referenced * character. The referenced character ranges do not to overlap or nest. The map is sorted by position. */ - def refEntity: SortedMap[Int, (TemplateEntity, Int)] + def refEntity: SortedMap[Int, (LinkTo, Int)] /** The human-readable representation of this type. */ override def toString = name - } diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala b/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala index ecc3273903..3f0024cb68 100644 --- a/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala +++ b/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala @@ -44,7 +44,6 @@ final case class Body(blocks: Seq[Block]) { case inlines => Some(Chain(inlines)) } } - } /** A block-level element of text, such as a paragraph or code block. */ @@ -67,10 +66,14 @@ final case class Bold(text: Inline) extends Inline final case class Underline(text: Inline) extends Inline final case class Superscript(text: Inline) extends Inline final case class Subscript(text: Inline) extends Inline -final case class EntityLink(target: String, template: () => Option[TemplateEntity]) extends Inline final case class Link(target: String, title: Inline) extends Inline final case class Monospace(text: Inline) extends Inline final case class Text(text: String) extends Inline +abstract class EntityLink(val title: Inline) extends Inline { def link: LinkTo } +object EntityLink { + def apply(title: Inline, linkTo: LinkTo) = new EntityLink(title) { def link: LinkTo = linkTo } + def unapply(el: EntityLink): Option[(Inline, LinkTo)] = Some((el.title, el.link)) +} final case class HtmlTag(data: String) extends Inline { def canClose(open: HtmlTag) = { open.data.stripPrefix("<") == data.stripPrefix("</") diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala b/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala index 7b70683db5..c8f4c2f285 100644 --- a/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala +++ b/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala @@ -114,6 +114,18 @@ abstract class Comment { /** A set of diagram directives for the content diagram */ def contentDiagram: List[String] + /** The group this member is part of */ + def group: Option[String] + + /** Member group descriptions */ + def groupDesc: Map[String,Body] + + /** Member group names (overriding the short tag) */ + def groupNames: Map[String,String] + + /** Member group priorities */ + def groupPrio: Map[String,Int] + override def toString = body.toString + "\n" + (authors map ("@author " + _.toString)).mkString("\n") + diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala index 2099315cc6..1a11964e37 100644 --- a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala @@ -23,7 +23,7 @@ import language.postfixOps * * @author Manohar Jonnalagedda * @author Gilles Dubochet */ -trait CommentFactory { thisFactory: ModelFactory with CommentFactory => +trait CommentFactory { thisFactory: ModelFactory with CommentFactory with MemberLookup=> val global: Global import global.{ reporter, definitions } @@ -31,16 +31,16 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => protected val commentCache = mutable.HashMap.empty[(global.Symbol, TemplateImpl), Comment] def addCommentBody(sym: global.Symbol, inTpl: TemplateImpl, docStr: String, docPos: global.Position): global.Symbol = { - commentCache += (sym, inTpl) -> parse(docStr, docStr, docPos) + commentCache += (sym, inTpl) -> parse(docStr, docStr, docPos, None) sym } - def comment(sym: global.Symbol, inTpl: DocTemplateImpl): Option[Comment] = { + def comment(sym: global.Symbol, currentTpl: Option[DocTemplateImpl], inTpl: DocTemplateImpl): Option[Comment] = { val key = (sym, inTpl) if (commentCache isDefinedAt key) Some(commentCache(key)) else { - val c = defineComment(sym, inTpl) + val c = defineComment(sym, currentTpl, inTpl) if (c isDefined) commentCache += (sym, inTpl) -> c.get c } @@ -50,7 +50,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => * cases we have to give some `inTpl` comments (parent class for example) * to the comment of the symbol. * This function manages some of those cases : Param accessor and Primary constructor */ - def defineComment(sym: global.Symbol, inTpl: DocTemplateImpl):Option[Comment] = { + def defineComment(sym: global.Symbol, currentTpl: Option[DocTemplateImpl], inTpl: DocTemplateImpl):Option[Comment] = { //param accessor case // We just need the @param argument, we put it into the body @@ -87,7 +87,8 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => else { val rawComment = global.expandedDocComment(sym, inTpl.sym).trim if (rawComment != "") { - val c = parse(rawComment, global.rawDocComment(sym), global.docCommentPos(sym)) + val tplOpt = if (currentTpl.isDefined) currentTpl else Some(inTpl) + val c = parse(rawComment, global.rawDocComment(sym), global.docCommentPos(sym), tplOpt) Some(c) } else None @@ -113,7 +114,11 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => constructor0: Option[Body] = None, source0: Option[String] = None, inheritDiagram0: List[String] = List.empty, - contentDiagram0: List[String] = List.empty + contentDiagram0: List[String] = List.empty, + group0: Option[Body] = None, + groupDesc0: Map[String,Body] = Map.empty, + groupNames0: Map[String,Body] = Map.empty, + groupPrio0: Map[String,Body] = Map.empty ) : Comment = new Comment{ val body = if(body0 isDefined) body0.get else Body(Seq.empty) val authors = authors0 @@ -132,6 +137,35 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => val source = source0 val inheritDiagram = inheritDiagram0 val contentDiagram = contentDiagram0 + val groupDesc = groupDesc0 + val group = + group0 match { + case Some(Body(List(Paragraph(Chain(List(Summary(Text(groupId)))))))) => Some(groupId.toString.trim) + case _ => None + } + val groupPrio = groupPrio0 flatMap { + case (group, body) => + try { + body match { + case Body(List(Paragraph(Chain(List(Summary(Text(prio))))))) => List(group -> prio.toInt) + case _ => List() + } + } catch { + case _: java.lang.NumberFormatException => List() + } + } + val groupNames = groupNames0 flatMap { + case (group, body) => + try { + body match { + case Body(List(Paragraph(Chain(List(Summary(Text(name))))))) if (!name.trim.contains("\n")) => List(group -> (name.trim)) + case _ => List() + } + } catch { + case _: java.lang.NumberFormatException => List() + } + } + } protected val endOfText = '\u0003' @@ -201,7 +235,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => /** A Scaladoc tag linked to a symbol. Returns the name of the tag, the name * of the symbol, and the rest of the line. */ protected val SymbolTag = - new Regex("""\s*@(param|tparam|throws)\s+(\S*)\s*(.*)""") + new Regex("""\s*@(param|tparam|throws|groupdesc|groupname|groupprio)\s+(\S*)\s*(.*)""") /** The start of a scaladoc code block */ protected val CodeBlockStart = @@ -225,7 +259,8 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => * @param comment The expanded comment string (including start and end markers) to be parsed. * @param src The raw comment source string. * @param pos The position of the comment in source. */ - protected def parse(comment: String, src: String, pos: Position): Comment = { + protected def parse(comment: String, src: String, pos: Position, inTplOpt: Option[DocTemplateImpl] = None): Comment = { + assert(!inTplOpt.isDefined || inTplOpt.get != null) /** The cleaned raw comment as a list of lines. Cleaning removes comment * start and end markers, line start markers and unnecessary whitespace. */ @@ -348,10 +383,11 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => case None => List.empty } - val tagsWithoutDiagram = tags.filterNot(pair => pair._1 == inheritDiagramTag || pair._1 == contentDiagramTag) + val stripTags=List(inheritDiagramTag, contentDiagramTag, SimpleTagKey("template"), SimpleTagKey("documentable")) + val tagsWithoutDiagram = tags.filterNot(pair => stripTags.contains(pair._1)) val bodyTags: mutable.Map[TagKey, List[Body]] = - mutable.Map(tagsWithoutDiagram mapValues {tag => tag map (parseWiki(_, pos))} toSeq: _*) + mutable.Map(tagsWithoutDiagram mapValues {tag => tag map (parseWiki(_, pos, inTplOpt))} toSeq: _*) def oneTag(key: SimpleTagKey): Option[Body] = ((bodyTags remove key): @unchecked) match { @@ -384,7 +420,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => } val com = createComment ( - body0 = Some(parseWiki(docBody.toString, pos)), + body0 = Some(parseWiki(docBody.toString, pos, inTplOpt)), authors0 = allTags(SimpleTagKey("author")), see0 = allTags(SimpleTagKey("see")), result0 = oneTag(SimpleTagKey("return")), @@ -400,7 +436,11 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => constructor0 = oneTag(SimpleTagKey("constructor")), source0 = Some(clean(src).mkString("\n")), inheritDiagram0 = inheritDiagramText, - contentDiagram0 = contentDiagramText + contentDiagram0 = contentDiagramText, + group0 = oneTag(SimpleTagKey("group")), + groupDesc0 = allSymsOneTag(SimpleTagKey("groupdesc")), + groupNames0 = allSymsOneTag(SimpleTagKey("groupname")), + groupPrio0 = allSymsOneTag(SimpleTagKey("groupprio")) ) for ((key, _) <- bodyTags) @@ -420,8 +460,10 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => * - Removed start-of-line star and one whitespace afterwards (if present). * - Removed all end-of-line whitespace. * - Only `endOfLine` is used to mark line endings. */ - def parseWiki(string: String, pos: Position): Body = { - new WikiParser(string, pos).document() + def parseWiki(string: String, pos: Position, inTplOpt: Option[DocTemplateImpl]): Body = { + assert(!inTplOpt.isDefined || inTplOpt.get != null) + + new WikiParser(string, pos, inTplOpt).document() } /** TODO @@ -429,7 +471,8 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => * @author Ingo Maier * @author Manohar Jonnalagedda * @author Gilles Dubochet */ - protected final class WikiParser(val buffer: String, pos: Position) extends CharReader(buffer) { wiki => + protected final class WikiParser(val buffer: String, pos: Position, inTplOpt: Option[DocTemplateImpl]) extends CharReader(buffer) { wiki => + assert(!inTplOpt.isDefined || inTplOpt.get != null) var summaryParsed = false @@ -617,7 +660,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => else if (check(",,")) subscript() else if (check("[[")) link() else { - readUntil { char == safeTagMarker || check("''") || char == '`' || check("__") || char == '^' || check(",,") || check("[[") || isInlineEnd || checkParaEnded || char == endOfLine } + readUntil { char == safeTagMarker || check("''") || char == '`' || check("__") || char == '^' || check(",,") || check("[[") || check("{{") || isInlineEnd || checkParaEnded || char == endOfLine } Text(getRead()) } } @@ -719,29 +762,27 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => def link(): Inline = { val SchemeUri = """([^:]+:.*)""".r jump("[[") - readUntil { check("]]") || check(" ") } + var parens = 1 + readUntil { parens += 1; !check("[") } + getRead // clear the buffer + val start = "[" * parens + val stop = "]" * parens + //println("link with " + parens + " matching parens") + readUntil { check(stop) || check(" ") } val target = getRead() val title = - if (!check("]]")) Some({ + if (!check(stop)) Some({ jump(" ") - inline(check("]]")) + inline(check(stop)) }) else None - jump("]]") + jump(stop) (target, title) match { case (SchemeUri(uri), optTitle) => Link(uri, optTitle getOrElse Text(uri)) case (qualName, optTitle) => - optTitle foreach (text => reportError(pos, "entity link to " + qualName + " cannot have a custom title'" + text + "'")) - // XXX rather than warning here we should allow unqualified names - // to refer to members of the same package. The "package exists" - // exclusion is because [[scala]] is used in some scaladoc. - if (!qualName.contains(".") && !definitions.packageExists(qualName)) - reportError(pos, "entity link to " + qualName + " should be a fully qualified name") - - // move the template resolution as late as possible - EntityLink(qualName, () => findTemplate(qualName)) + makeEntityLink(optTitle getOrElse Text(target), pos, target, inTplOpt) } } diff --git a/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala b/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala index 8527ca4039..c2aa1f17f3 100644 --- a/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala +++ b/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala @@ -13,18 +13,18 @@ import model._ abstract class Diagram { def nodes: List[Node] def edges: List[(Node, List[Node])] - def isPackageDiagram = false - def isClassDiagram = false + def isContentDiagram = false // Implemented by ContentDiagram + def isInheritanceDiagram = false // Implemented by InheritanceDiagram def depthInfo: DepthInfo } -case class PackageDiagram(nodes:List[/*Class*/Node], edges:List[(Node, List[Node])]) extends Diagram { - override def isPackageDiagram = true - lazy val depthInfo = new PackageDiagramDepth(this) +case class ContentDiagram(nodes:List[/*Class*/Node], edges:List[(Node, List[Node])]) extends Diagram { + override def isContentDiagram = true + lazy val depthInfo = new ContentDiagramDepth(this) } /** A class diagram */ -case class ClassDiagram(thisNode: ThisNode, +case class InheritanceDiagram(thisNode: ThisNode, superClasses: List[/*Class*/Node], subClasses: List[/*Class*/Node], incomingImplicits: List[ImplicitNode], @@ -33,7 +33,7 @@ case class ClassDiagram(thisNode: ThisNode, def edges = (thisNode -> (superClasses ::: outgoingImplicits)) :: (subClasses ::: incomingImplicits).map(_ -> List(thisNode)) - override def isClassDiagram = true + override def isInheritanceDiagram = true lazy val depthInfo = new DepthInfo { def maxDepth = 3 def nodeDepth(node: Node) = @@ -70,7 +70,8 @@ abstract class Node { def isClassNode = if (tpl.isDefined) (tpl.get.isClass || tpl.get.qualifiedName == "scala.AnyRef") else false def isTraitNode = if (tpl.isDefined) tpl.get.isTrait else false def isObjectNode= if (tpl.isDefined) tpl.get.isObject else false - def isOtherNode = !(isClassNode || isTraitNode || isObjectNode) + def isTypeNode = if (doctpl.isDefined) doctpl.get.isAbstractType || doctpl.get.isAliasType else false + def isOtherNode = !(isClassNode || isTraitNode || isObjectNode || isTypeNode) def isImplicitNode = false def isOutsideNode = false def tooltip: Option[String] @@ -91,6 +92,7 @@ abstract class Node { object Node { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = Some((n.tpe, n.tpl)) } object ClassNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isClassNode) Some((n.tpe, n.tpl)) else None } object TraitNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isTraitNode) Some((n.tpe, n.tpl)) else None } +object TypeNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isTypeNode) Some((n.tpe, n.tpl)) else None } object ObjectNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isObjectNode) Some((n.tpe, n.tpl)) else None } object OutsideNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isOutsideNode) Some((n.tpe, n.tpl)) else None } object OtherNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isOtherNode) Some((n.tpe, n.tpl)) else None } @@ -98,24 +100,24 @@ object OtherNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEn /** The node for the current class */ -case class ThisNode(tpe: TypeEntity, tpl: Option[TemplateEntity], tooltip: Option[String] = None) extends Node { override def isThisNode = true } +case class ThisNode(tpe: TypeEntity, tpl: Option[TemplateEntity])(val tooltip: Option[String] = None) extends Node { override def isThisNode = true } /** The usual node */ -case class NormalNode(tpe: TypeEntity, tpl: Option[TemplateEntity], tooltip: Option[String] = None) extends Node { override def isNormalNode = true } +case class NormalNode(tpe: TypeEntity, tpl: Option[TemplateEntity])(val tooltip: Option[String] = None) extends Node { override def isNormalNode = true } /** A class or trait the thisnode can be converted to by an implicit conversion * TODO: I think it makes more sense to use the tpe links to templates instead of the TemplateEntity for implicit nodes * since some implicit conversions convert the class to complex types that cannot be represented as a single tmeplate */ -case class ImplicitNode(tpe: TypeEntity, tpl: Option[TemplateEntity], tooltip: Option[String] = None) extends Node { override def isImplicitNode = true } +case class ImplicitNode(tpe: TypeEntity, tpl: Option[TemplateEntity])(val tooltip: Option[String] = None) extends Node { override def isImplicitNode = true } /** An outside node is shown in packages when a class from a different package makes it to the package diagram due to * its relation to a class in the template (see @contentDiagram hideInheritedNodes annotation) */ -case class OutsideNode(tpe: TypeEntity, tpl: Option[TemplateEntity], tooltip: Option[String] = None) extends Node { override def isOutsideNode = true } +case class OutsideNode(tpe: TypeEntity, tpl: Option[TemplateEntity])(val tooltip: Option[String] = None) extends Node { override def isOutsideNode = true } // Computing and offering node depth information -class PackageDiagramDepth(pack: PackageDiagram) extends DepthInfo { +class ContentDiagramDepth(pack: ContentDiagram) extends DepthInfo { private[this] var _maxDepth = 0 private[this] var _nodeDepth = Map[Node, Int]() private[this] var seedNodes = Set[Node]() diff --git a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala index 1a8ad193aa..2645d8fd14 100644 --- a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala @@ -18,21 +18,14 @@ import scala.collection.immutable.SortedMap * @author Vlad Ureche */ trait DiagramFactory extends DiagramDirectiveParser { - this: ModelFactory with DiagramFactory with CommentFactory with TreeFactory => + this: ModelFactory with ModelFactoryTypeSupport with DiagramFactory with CommentFactory with TreeFactory => import this.global.definitions._ import this.global._ // the following can used for hardcoding different relations into the diagram, for bootstrapping purposes - lazy val AnyNode = normalNode(AnyClass) - lazy val AnyRefNode = normalNode(AnyRefClass) - lazy val AnyValNode = normalNode(AnyValClass) - lazy val NullNode = normalNode(NullClass) - lazy val NothingNode = normalNode(NothingClass) - def normalNode(sym: Symbol) = - NormalNode(makeTemplate(sym).ownType, Some(makeTemplate(sym))) def aggregationNode(text: String) = - NormalNode(new TypeEntity { val name = text; val refEntity = SortedMap[Int, (TemplateEntity, Int)]() }, None) + NormalNode(new TypeEntity { val name = text; val refEntity = SortedMap[Int, (LinkTo, Int)]() }, None)() /** Create the inheritance diagram for this template */ def makeInheritanceDiagram(tpl: DocTemplateImpl): Option[Diagram] = { @@ -52,31 +45,30 @@ trait DiagramFactory extends DiagramDirectiveParser { None else { // the main node - val thisNode = ThisNode(tpl.ownType, Some(tpl), Some(tpl.qualifiedName + " (this " + tpl.kind + ")")) + val thisNode = ThisNode(tpl.resultType, Some(tpl))(Some(tpl.qualifiedName + " (this " + tpl.kind + ")")) // superclasses var superclasses: List[Node] = tpl.parentTypes.collect { - case p: (TemplateEntity, TypeEntity) if !classExcluded(p._1) => NormalNode(p._2, Some(p._1)) + case p: (TemplateEntity, TypeEntity) if !classExcluded(p._1) => NormalNode(p._2, Some(p._1))() }.reverse // incoming implcit conversions lazy val incomingImplicitNodes = tpl.incomingImplicitlyConvertedClasses.map { case (incomingTpl, conv) => - ImplicitNode(incomingTpl.ownType, Some(incomingTpl), implicitTooltip(from=incomingTpl, to=tpl, conv=conv)) + ImplicitNode(makeType(incomingTpl.sym.tpe, tpl), Some(incomingTpl))(implicitTooltip(from=incomingTpl, to=tpl, conv=conv)) } // subclasses var subclasses: List[Node] = - tpl.directSubClasses.flatMap { - case d: TemplateEntity if !classExcluded(d) => List(NormalNode(d.ownType, Some(d))) - case _ => Nil + tpl.directSubClasses.collect { + case d: TemplateImpl if !classExcluded(d) => NormalNode(makeType(d.sym.tpe, tpl), Some(d))() }.sortBy(_.tpl.get.name)(implicitly[Ordering[String]].reverse) // outgoing implicit coversions lazy val outgoingImplicitNodes = tpl.outgoingImplicitlyConvertedClasses.map { case (outgoingTpl, outgoingType, conv) => - ImplicitNode(outgoingType, Some(outgoingTpl), implicitTooltip(from=tpl, to=tpl, conv=conv)) + ImplicitNode(outgoingType, Some(outgoingTpl))(implicitTooltip(from=tpl, to=tpl, conv=conv)) } // TODO: Everyone should be able to use the @{inherit,content}Diagram annotation to change the diagrams. @@ -93,7 +85,7 @@ trait DiagramFactory extends DiagramDirectiveParser { val filteredImplicitOutgoingNodes = if (diagramFilter.hideOutgoingImplicits) Nil else outgoingImplicitNodes // final diagram filter - filterDiagram(ClassDiagram(thisNode, filteredSuperclasses.reverse, filteredSubclasses.reverse, filteredIncomingImplicits, filteredImplicitOutgoingNodes), diagramFilter) + filterDiagram(InheritanceDiagram(thisNode, filteredSuperclasses.reverse, filteredSubclasses.reverse, filteredIncomingImplicits, filteredImplicitOutgoingNodes), diagramFilter) } tModel += System.currentTimeMillis @@ -129,12 +121,10 @@ trait DiagramFactory extends DiagramDirectiveParser { // for each node, add its subclasses for (node <- nodesAll if !classExcluded(node)) { node match { - case dnode: DocTemplateImpl => - var superClasses = dnode.parentTypes.map(_._1) + case dnode: MemberTemplateImpl => + var superClasses = dnode.parentTypes.map(_._1).filter(nodesAll.contains(_)) - superClasses = superClasses.filter(nodesAll.contains(_)) - - // TODO: Everyone should be able to use the @{inherit,content}Diagram annotation to change the diagrams. + // TODO: Everyone should be able to use the @{inherit,content}Diagram annotation to add nodes to diagrams. if (pack.sym == ScalaPackage) if (dnode.sym == NullClass) superClasses = List(makeTemplate(AnyRefClass)) @@ -149,7 +139,12 @@ trait DiagramFactory extends DiagramDirectiveParser { case _ => } - mapNodes += node -> (if (node.inTemplate == pack) NormalNode(node.ownType, Some(node)) else OutsideNode(node.ownType, Some(node))) + mapNodes += node -> ( + if (node.inTemplate == pack && (node.isDocTemplate || node.isAbstractType || node.isAliasType)) + NormalNode(node.resultType, Some(node))() + else + OutsideNode(node.resultType, Some(node))() + ) } if (nodesShown.isEmpty) @@ -173,9 +168,12 @@ trait DiagramFactory extends DiagramDirectiveParser { val anyRefSubtypes = Nil val allAnyRefTypes = aggregationNode("All AnyRef subtypes") val nullTemplate = makeTemplate(NullClass) - PackageDiagram(allAnyRefTypes::nodes, (mapNodes(nullTemplate), allAnyRefTypes::anyRefSubtypes)::edges.filterNot(_._1.tpl == Some(nullTemplate))) + if (nullTemplate.isDocTemplate) + ContentDiagram(allAnyRefTypes::nodes, (mapNodes(nullTemplate), allAnyRefTypes::anyRefSubtypes)::edges.filterNot(_._1.tpl == Some(nullTemplate))) + else + ContentDiagram(nodes, edges) } else - PackageDiagram(nodes, edges) + ContentDiagram(nodes, edges) filterDiagram(diagram, diagramFilter) } @@ -200,10 +198,10 @@ trait DiagramFactory extends DiagramDirectiveParser { else { // Final diagram, with the filtered nodes and edges diagram match { - case ClassDiagram(thisNode, _, _, _, _) if diagramFilter.hideNode(thisNode) => + case InheritanceDiagram(thisNode, _, _, _, _) if diagramFilter.hideNode(thisNode) => None - case ClassDiagram(thisNode, superClasses, subClasses, incomingImplicits, outgoingImplicits) => + case InheritanceDiagram(thisNode, superClasses, subClasses, incomingImplicits, outgoingImplicits) => def hideIncoming(node: Node): Boolean = diagramFilter.hideNode(node) || diagramFilter.hideEdge(node, thisNode) @@ -214,13 +212,13 @@ trait DiagramFactory extends DiagramDirectiveParser { // println(thisNode) // println(superClasses.map(cl => "super: " + cl + " " + hideOutgoing(cl)).mkString("\n")) // println(subClasses.map(cl => "sub: " + cl + " " + hideIncoming(cl)).mkString("\n")) - Some(ClassDiagram(thisNode, + Some(InheritanceDiagram(thisNode, superClasses.filterNot(hideOutgoing(_)), subClasses.filterNot(hideIncoming(_)), incomingImplicits.filterNot(hideIncoming(_)), outgoingImplicits.filterNot(hideOutgoing(_)))) - case PackageDiagram(nodes0, edges0) => + case ContentDiagram(nodes0, edges0) => // Filter out all edges that: // (1) are sources of hidden classes // (2) are manually hidden by the user @@ -242,7 +240,7 @@ trait DiagramFactory extends DiagramDirectiveParser { val sourceNodes = edges.map(_._1) val sinkNodes = edges.map(_._2).flatten val nodes = (sourceNodes ::: sinkNodes).distinct - Some(PackageDiagram(nodes, edges)) + Some(ContentDiagram(nodes, edges)) } } diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index 8f287a5c7a..2a435aa6f6 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -627,7 +627,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") response raise ex throw ex - case ex => + case ex: Throwable => if (debugIDE) { println("exception thrown during response: "+ex) ex.printStackTrace() diff --git a/src/compiler/scala/tools/nsc/interactive/PresentationCompilerThread.scala b/src/compiler/scala/tools/nsc/interactive/PresentationCompilerThread.scala index 098884dab1..70d8a826d0 100644 --- a/src/compiler/scala/tools/nsc/interactive/PresentationCompilerThread.scala +++ b/src/compiler/scala/tools/nsc/interactive/PresentationCompilerThread.scala @@ -36,7 +36,7 @@ final class PresentationCompilerThread(var compiler: Global, name: String = "") // make sure we don't keep around stale instances compiler = null - case ex => + case ex: Throwable => compiler.log.flush() ex match { diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala index e5e7d7081d..b567293a3f 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala @@ -25,7 +25,7 @@ import ScalaClassLoader._ import scala.tools.util._ import language.{implicitConversions, existentials} import scala.reflect.{ClassTag, classTag} -import scala.tools.reflect.StdTags._ +import scala.tools.reflect.StdRuntimeTags._ /** The Scala interactive shell. It provides a read-eval-print loop * around the Interpreter class. diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index b385787cce..7bdbff8627 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -28,7 +28,7 @@ import typechecker.Analyzer import language.implicitConversions import scala.reflect.runtime.{ universe => ru } import scala.reflect.{ ClassTag, classTag } -import scala.tools.reflect.StdTags._ +import scala.tools.reflect.StdRuntimeTags._ /** directory to save .class files to */ private class ReplVirtualDirectory(out: JPrintWriter) extends VirtualDirectory("(memory)", None) { diff --git a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala index f0ee8b11f3..0991577829 100644 --- a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala @@ -40,9 +40,9 @@ trait StandardScalaSettings { val nowarn = BooleanSetting ("-nowarn", "Generate no warnings.") val optimise: BooleanSetting // depends on post hook which mutates other settings val print = BooleanSetting ("-print", "Print program with Scala-specific features removed.") - val target = ChoiceSetting ("-target", "target", "Target platform for object files.", + val target = ChoiceSetting ("-target", "target", "Target platform for object files. All JVM 1.5 targets are deprecated.", List("jvm-1.5", "jvm-1.5-fjbg", "jvm-1.5-asm", "jvm-1.6", "jvm-1.7", "msil"), - "jvm-1.5-asm") + "jvm-1.6") val unchecked = BooleanSetting ("-unchecked", "Enable detailed unchecked (erasure) warnings.") val uniqid = BooleanSetting ("-uniqid", "Uniquely tag all identifiers in debugging output.") val usejavacp = BooleanSetting ("-usejavacp", "Utilize the java.class.path in classpath resolution.") diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index bbdf10a021..e672f1914a 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -23,9 +23,12 @@ abstract class CleanUp extends Transform with ast.TreeDSL { new CleanUpTransformer(unit) class CleanUpTransformer(unit: CompilationUnit) extends Transformer { - private val newStaticMembers = mutable.Buffer.empty[Tree] - private val newStaticInits = mutable.Buffer.empty[Tree] - private val symbolsStoredAsStatic = mutable.Map.empty[String, Symbol] + private val newStaticMembers = mutable.Buffer.empty[Tree] + private val newStaticInits = mutable.Buffer.empty[Tree] + private val symbolsStoredAsStatic = mutable.Map.empty[String, Symbol] + private val staticBodies = mutable.Map.empty[(Symbol, Symbol), Tree] + private val syntheticClasses = mutable.Map.empty[Symbol, mutable.Set[Tree]] // package and trees + private val classNames = mutable.Map.empty[Symbol, Set[Name]] private def clearStatics() { newStaticMembers.clear() newStaticInits.clear() @@ -45,15 +48,16 @@ abstract class CleanUp extends Transform with ast.TreeDSL { result } private def transformTemplate(tree: Tree) = { - val Template(parents, self, body) = tree + val t @ Template(parents, self, body) = tree clearStatics() + val newBody = transformTrees(body) val templ = deriveTemplate(tree)(_ => transformTrees(newStaticMembers.toList) ::: newBody) try addStaticInits(templ) // postprocess to include static ctors finally clearStatics() } private def mkTerm(prefix: String): TermName = unit.freshTermName(prefix) - + /** Kludge to provide a safe fix for #4560: * If we generate a reference in an implementation class, we * watch out for embedded This(..) nodes that point to the interface. @@ -555,7 +559,73 @@ abstract class CleanUp extends Transform with ast.TreeDSL { else tree } - + + case ValDef(mods, name, tpt, rhs) if tree.symbol.hasStaticAnnotation => + log("moving @static valdef field: " + name + ", in: " + tree.symbol.owner) + val sym = tree.symbol + val owner = sym.owner + + val staticBeforeLifting = atPhase(currentRun.erasurePhase) { owner.isStatic } + val isPrivate = atPhase(currentRun.typerPhase) { sym.getter(owner).hasFlag(PRIVATE) } + val isProtected = atPhase(currentRun.typerPhase) { sym.getter(owner).hasFlag(PROTECTED) } + val isLazy = atPhase(currentRun.typerPhase) { sym.getter(owner).hasFlag(LAZY) } + if (!owner.isModuleClass || !staticBeforeLifting) { + if (!sym.isSynthetic) { + reporter.error(tree.pos, "Only members of top-level objects and their nested objects can be annotated with @static.") + tree.symbol.removeAnnotation(StaticClass) + } + super.transform(tree) + } else if (isPrivate || isProtected) { + reporter.error(tree.pos, "The @static annotation is only allowed on public members.") + tree.symbol.removeAnnotation(StaticClass) + super.transform(tree) + } else if (isLazy) { + reporter.error(tree.pos, "The @static annotation is not allowed on lazy members.") + tree.symbol.removeAnnotation(StaticClass) + super.transform(tree) + } else if (owner.isModuleClass) { + val linkedClass = owner.companionClass match { + case NoSymbol => + // create the companion class if it does not exist + val enclosing = owner.owner + val compclass = enclosing.newClass(newTypeName(owner.name.toString)) + compclass setInfo ClassInfoType(List(ObjectClass.tpe), newScope, compclass) + enclosing.info.decls enter compclass + + val compclstree = ClassDef(compclass, NoMods, List(List()), List(List()), List(), tree.pos) + + syntheticClasses.getOrElseUpdate(enclosing, mutable.Set()) += compclstree + + compclass + case comp => comp + } + + // create a static field in the companion class for this @static field + val stfieldSym = linkedClass.newVariable(newTermName(name), tree.pos, STATIC | SYNTHETIC | FINAL) setInfo sym.tpe + stfieldSym.addAnnotation(StaticClass) + + val names = classNames.getOrElseUpdate(linkedClass, linkedClass.info.decls.collect { + case sym if sym.name.isTermName => sym.name + } toSet) + if (names(stfieldSym.name)) { + reporter.error( + tree.pos, + "@static annotated field " + tree.symbol.name + " has the same name as a member of class " + linkedClass.name + ) + } else { + linkedClass.info.decls enter stfieldSym + + val initializerBody = rhs + + // static field was previously initialized in the companion object itself, like this: + // staticBodies((linkedClass, stfieldSym)) = Select(This(owner), sym.getter(owner)) + // instead, we move the initializer to the static ctor of the companion class + // we save the entire ValDef/DefDef to extract the rhs later + staticBodies((linkedClass, stfieldSym)) = tree + } + } + super.transform(tree) + /* MSIL requires that the stack is empty at the end of a try-block. * Hence, we here rewrite all try blocks with a result != {Unit, All} such that they * store their result in a local variable. The catch blocks are adjusted as well. @@ -665,6 +735,11 @@ abstract class CleanUp extends Transform with ast.TreeDSL { if (newStaticInits.isEmpty) template else { + val ctorBody = newStaticInits.toList flatMap { + case Block(stats, expr) => stats :+ expr + case t => List(t) + } + val newCtor = findStaticCtor(template) match { // in case there already were static ctors - augment existing ones // currently, however, static ctors aren't being generated anywhere else @@ -673,22 +748,76 @@ abstract class CleanUp extends Transform with ast.TreeDSL { deriveDefDef(ctor) { case block @ Block(stats, expr) => // need to add inits to existing block - treeCopy.Block(block, newStaticInits.toList ::: stats, expr) + treeCopy.Block(block, ctorBody ::: stats, expr) case term: TermTree => // need to create a new block with inits and the old term - treeCopy.Block(term, newStaticInits.toList, term) + treeCopy.Block(term, ctorBody, term) } case _ => // create new static ctor val staticCtorSym = currentClass.newStaticConstructor(template.pos) - val rhs = Block(newStaticInits.toList, Literal(Constant(()))) + val rhs = Block(ctorBody, Literal(Constant(()))) localTyper.typedPos(template.pos)(DefDef(staticCtorSym, rhs)) } deriveTemplate(template)(newCtor :: _) } } - + + private def addStaticDeclarations(tree: Template, clazz: Symbol) { + // add static field initializer statements for each static field in clazz + if (!clazz.isModuleClass) for { + staticSym <- clazz.info.decls + if staticSym.hasStaticAnnotation + } staticSym match { + case stfieldSym if stfieldSym.isVariable => + val valdef = staticBodies((clazz, stfieldSym)) + val ValDef(_, _, _, rhs) = valdef + val fixedrhs = rhs.changeOwner((valdef.symbol, clazz.info.decl(nme.CONSTRUCTOR))) + + val stfieldDef = localTyper.typedPos(tree.pos)(VAL(stfieldSym) === EmptyTree) + val flattenedInit = fixedrhs match { + case Block(stats, expr) => Block(stats, safeREF(stfieldSym) === expr) + case rhs => safeREF(stfieldSym) === rhs + } + val stfieldInit = localTyper.typedPos(tree.pos)(flattenedInit) + + // add field definition to new defs + newStaticMembers append stfieldDef + newStaticInits append stfieldInit + } + } + + + + override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = { + super.transformStats(stats, exprOwner) ++ { + // flush pending synthetic classes created in this owner + val synthclassdefs = syntheticClasses.get(exprOwner).toList.flatten + syntheticClasses -= exprOwner + synthclassdefs map { + cdef => localTyper.typedPos(cdef.pos)(cdef) + } + } map { + case clsdef @ ClassDef(mods, name, tparams, t @ Template(parent, self, body)) => + // process all classes in the package again to add static initializers + clearStatics() + + addStaticDeclarations(t, clsdef.symbol) + + val templ = deriveTemplate(t)(_ => transformTrees(newStaticMembers.toList) ::: body) + val ntempl = + try addStaticInits(templ) + finally clearStatics() + + val derived = deriveClassDef(clsdef)(_ => ntempl) + classNames.remove(clsdef.symbol) + derived + + case stat => stat + } + } + } // CleanUpTransformer } diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index e5119eac71..70bd0bd21b 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -186,12 +186,15 @@ abstract class Constructors extends Transform with ast.TreeDSL { // before the superclass constructor call, otherwise it goes after. // Lazy vals don't get the assignment in the constructor. if (!stat.symbol.tpe.isInstanceOf[ConstantType]) { - if (rhs != EmptyTree && !stat.symbol.isLazy) { + if (stat.symbol.hasStaticAnnotation) { + debuglog("@static annotated field initialization skipped.") + defBuf += deriveValDef(stat)(tree => tree) + } else if (rhs != EmptyTree && !stat.symbol.isLazy) { val rhs1 = intoConstructor(stat.symbol, rhs); (if (canBeMoved(stat)) constrPrefixBuf else constrStatBuf) += mkAssign( stat.symbol, rhs1) + defBuf += deriveValDef(stat)(_ => EmptyTree) } - defBuf += deriveValDef(stat)(_ => EmptyTree) } case ClassDef(_, _, _, _) => // classes are treated recursively, and left in the template diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index ab7bbc591b..afbe528b1f 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -46,10 +46,13 @@ abstract class ExplicitOuter extends InfoTransform private def haveSameOuter(parent: Type, clazz: Symbol) = parent match { case TypeRef(pre, sym, _) => val owner = clazz.owner + + //println(s"have same outer $parent $clazz $sym ${sym.owner} $owner $pre") sym.isClass && owner.isClass && - owner == sym.owner && + (owner isSubClass sym.owner) && owner.thisType =:= pre + case _ => false } @@ -480,7 +483,7 @@ abstract class ExplicitOuter extends InfoTransform val vparamss1 = if (isInner(clazz)) { // (4) val outerParam = - sym.newValueParameter(nme.OUTER, sym.pos) setInfo outerField(clazz).info + sym.newValueParameter(nme.OUTER, sym.pos) setInfo clazz.outerClass.thisType ((ValDef(outerParam) setType NoType) :: vparamss.head) :: vparamss.tail } else vparamss super.transform(copyDefDef(tree)(vparamss = vparamss1)) diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index d5bbc578fc..492273dfdc 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -397,6 +397,9 @@ abstract class TailCalls extends Transform { case Apply(fun, arg :: Nil) if hasSynthCaseSymbol(fun) && tailLabels(fun.symbol) => traverse(arg) + case Apply(fun, args) if (fun.symbol == Boolean_or || fun.symbol == Boolean_and) => + traverseTrees(args) + // a translated casedef case LabelDef(_, _, body) if hasSynthCaseSymbol(tree) => traverse(body) diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index b7043e58de..d33857371d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -1140,7 +1140,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { } try macroExpandInternal - catch { case ex => handleMacroExpansionException(typer, expandee, ex) } + catch { case ex: Throwable => handleMacroExpansionException(typer, expandee, ex) } } private def macroExpandWithoutRuntime(typer: Typer, expandee: Tree): MacroExpansionResult = { diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index b54f127417..43edad3576 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -54,6 +54,25 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } import debugging.patmatDebug + // to govern how much time we spend analyzing matches for unreachability/exhaustivity + object AnalysisBudget { + import scala.tools.cmd.FromString.IntFromString + val max = sys.props.get("scalac.patmat.analysisBudget").collect(IntFromString.orElse{case "off" => Integer.MAX_VALUE}).getOrElse(256) + + abstract class Exception extends RuntimeException("CNF budget exceeded") { + val advice: String + def warn(pos: Position, kind: String) = currentUnit.uncheckedWarning(pos, s"Cannot check match for $kind.\n$advice") + } + + object exceeded extends Exception { + val advice = s"(The analysis required more space than allowed. Please try with scalac -Dscalac.patmat.analysisBudget=${AnalysisBudget.max*2} or -Dscalac.patmat.analysisBudget=off.)" + } + + object stackOverflow extends Exception { + val advice = "(There was a stack overflow. Please try increasing the stack available to the compiler using e.g., -Xss2m.)" + } + } + def newTransformer(unit: CompilationUnit): Transformer = if (opt.virtPatmat) new MatchTransformer(unit) else noopTransformer @@ -422,7 +441,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL The pattern matches any value v such that r == v (§12.1). The type of r must conform to the expected type of the pattern. **/ - case Literal(Constant(_)) | Ident(_) | Select(_, _) => + case Literal(Constant(_)) | Ident(_) | Select(_, _) | This(_) => noFurtherSubPats(EqualityTestTreeMaker(patBinder, patTree, pos)) case Alternative(alts) => @@ -439,7 +458,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL patmatDebug("WARNING: Bind tree with unbound symbol "+ patTree) noFurtherSubPats() // there's no symbol -- something's wrong... don't fail here though (or should we?) - // case Star(_) | ArrayValue | This => error("stone age pattern relics encountered!") + // case Star(_) | ArrayValue => error("stone age pattern relics encountered!") case _ => error("unsupported pattern: "+ patTree +"(a "+ patTree.getClass +")") @@ -1946,14 +1965,14 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL type Formula def andFormula(a: Formula, b: Formula): Formula - class CNFBudgetExceeded extends RuntimeException("CNF budget exceeded") - // may throw an CNFBudgetExceeded - def propToSolvable(p: Prop) = { + // may throw an AnalysisBudget.Exception + def propToSolvable(p: Prop): Formula = { val (eqAxioms, pure :: Nil) = removeVarEq(List(p), modelNull = false) eqFreePropToSolvable(And(eqAxioms, pure)) } + // may throw an AnalysisBudget.Exception def eqFreePropToSolvable(p: Prop): Formula def cnfString(f: Formula): String @@ -1979,7 +1998,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL type Lit def Lit(sym: Sym, pos: Boolean = true): Lit - // throws an CNFBudgetExceeded when the prop results in a CNF that's too big + // throws an AnalysisBudget.Exception when the prop results in a CNF that's too big def eqFreePropToSolvable(p: Prop): Formula = { // TODO: for now, reusing the normalization from DPLL def negationNormalForm(p: Prop): Prop = p match { @@ -2001,9 +2020,9 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def lit(s: Sym) = formula(clause(Lit(s))) def negLit(s: Sym) = formula(clause(Lit(s, false))) - def conjunctiveNormalForm(p: Prop, budget: Int = 256): Formula = { + def conjunctiveNormalForm(p: Prop, budget: Int = AnalysisBudget.max): Formula = { def distribute(a: Formula, b: Formula, budget: Int): Formula = - if (budget <= 0) throw new CNFBudgetExceeded + if (budget <= 0) throw AnalysisBudget.exceeded else (a, b) match { // true \/ _ = true @@ -2018,7 +2037,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL big flatMap (c => distribute(formula(c), small, budget - (big.size*small.size))) } - if (budget <= 0) throw new CNFBudgetExceeded + if (budget <= 0) throw AnalysisBudget.exceeded p match { case True => TrueF @@ -2037,9 +2056,17 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } val start = Statistics.startTimer(patmatCNF) - val res = conjunctiveNormalForm(negationNormalForm(p)) + val res = + try { + conjunctiveNormalForm(negationNormalForm(p)) + } catch { case ex : StackOverflowError => + throw AnalysisBudget.stackOverflow + } + Statistics.stopTimer(patmatCNF, start) - patmatCNFSizes(res.size).value += 1 + + // + if (Statistics.enabled) patmatCNFSizes(res.size).value += 1 // patmatDebug("cnf for\n"+ p +"\nis:\n"+cnfString(res)) res @@ -2440,6 +2467,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // right now hackily implement this by pruning counter-examples // unreachability would also benefit from a more faithful representation + // reachability (dead code) // computes the first 0-based case index that is unreachable (if any) @@ -2508,9 +2536,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL if (reachable) None else Some(caseIndex) } catch { - case e : CNFBudgetExceeded => -// debugWarn(util.Position.formatMessage(prevBinder.pos, "Cannot check match for reachability", false)) -// e.printStackTrace() + case ex: AnalysisBudget.Exception => + ex.warn(prevBinder.pos, "unreachability") None // CNF budget exceeded } } @@ -2651,9 +2678,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL Statistics.stopTimer(patmatAnaExhaust, start) pruned } catch { - case e : CNFBudgetExceeded => - patmatDebug(util.Position.formatMessage(prevBinder.pos, "Cannot check match for exhaustivity", false)) - // e.printStackTrace() + case ex : AnalysisBudget.Exception => + ex.warn(prevBinder.pos, "exhaustivity") Nil // CNF budget exceeded } } diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index b0956446a7..a378a95786 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -129,7 +129,7 @@ abstract class TreeCheckers extends Analyzer { private def wrap[T](msg: => Any)(body: => Unit) { try body - catch { case x => + catch { case x: Throwable => Console.println("Caught " + x) Console.println(msg) x.printStackTrace diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index f4af6a6497..dbe65c16d8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1972,7 +1972,8 @@ trait Typers extends Modes with Adaptations with Tags { case SilentResultValue(tpt) => val alias = enclClass.newAliasType(name.toTypeName, useCase.pos) val tparams = cloneSymbolsAtOwner(tpt.tpe.typeSymbol.typeParams, alias) - alias setInfo typeFun(tparams, appliedType(tpt.tpe, tparams map (_.tpe))) + val newInfo = genPolyType(tparams, appliedType(tpt.tpe, tparams map (_.tpe))) + alias setInfo newInfo context.scope.enter(alias) case _ => } diff --git a/src/compiler/scala/tools/nsc/util/DocStrings.scala b/src/compiler/scala/tools/nsc/util/DocStrings.scala index f4ce6d6ef1..c88414c423 100755 --- a/src/compiler/scala/tools/nsc/util/DocStrings.scala +++ b/src/compiler/scala/tools/nsc/util/DocStrings.scala @@ -144,6 +144,12 @@ object DocStrings { } /** Optionally start and end index of return section in `str`, or `None` + * if `str` does not have a @group. */ + def groupDoc(str: String, sections: List[(Int, Int)]): Option[(Int, Int)] = + sections find (startsWithTag(str, _, "@group")) + + + /** Optionally start and end index of return section in `str`, or `None` * if `str` does not have a @return. */ def returnDoc(str: String, sections: List[(Int, Int)]): Option[(Int, Int)] = diff --git a/src/compiler/scala/tools/nsc/util/InterruptReq.scala b/src/compiler/scala/tools/nsc/util/InterruptReq.scala index 2857823ceb..61aaa1bdcb 100644 --- a/src/compiler/scala/tools/nsc/util/InterruptReq.scala +++ b/src/compiler/scala/tools/nsc/util/InterruptReq.scala @@ -19,9 +19,10 @@ abstract class InterruptReq { try { result = Some(Left(todo())) } catch { - case t => result = Some(Right(t)) + case t: Throwable => result = Some(Right(t)) + } finally { + notify() } - notify() } /** To be called from interrupting client to get result for interrupt */ diff --git a/src/compiler/scala/tools/reflect/FastTrack.scala b/src/compiler/scala/tools/reflect/FastTrack.scala index 63ecfa32b2..237ef813c7 100644 --- a/src/compiler/scala/tools/reflect/FastTrack.scala +++ b/src/compiler/scala/tools/reflect/FastTrack.scala @@ -1,7 +1,6 @@ package scala.tools package reflect -import scala.reflect.makro.runtime.ContextReifiers import scala.reflect.reify.Taggers import scala.tools.nsc.typechecker.{Analyzer, Macros} @@ -16,7 +15,6 @@ trait FastTrack { import language.implicitConversions private implicit def context2taggers(c0: MacroContext): Taggers { val c: c0.type } = new { val c: c0.type = c0 } with Taggers - private implicit def context2contextreifiers(c0: MacroContext): ContextReifiers { val c: c0.type } = new { val c: c0.type = c0 } with ContextReifiers private implicit def context2macroimplementations(c0: MacroContext): MacroImplementations { val c: c0.type } = new { val c: c0.type = c0 } with MacroImplementations implicit def fastTrackEntry2MacroRuntime(entry: FastTrackEntry): MacroRuntime = args => entry.run(args) @@ -41,7 +39,6 @@ trait FastTrack { MacroInternal_materializeAbsTypeTag bindTo { case (c, Apply(TypeApply(_, List(tt)), List(u))) => c.materializeTypeTag(u, EmptyTree, tt.tpe, concrete = false) } MacroInternal_materializeTypeTag bindTo { case (c, Apply(TypeApply(_, List(tt)), List(u))) => c.materializeTypeTag(u, EmptyTree, tt.tpe, concrete = true) } ApiUniverseReify bindTo { case (c, Apply(TypeApply(_, List(tt)), List(expr))) => c.materializeExpr(c.prefix.tree, EmptyTree, expr) } - MacroContextReify bindTo { case (c, Apply(TypeApply(_, List(tt)), List(expr))) => c.materializeExprForMacroContext(c.prefix.tree, expr) } ReflectRuntimeCurrentMirror bindTo { case (c, _) => scala.reflect.runtime.Macros.currentMirror(c).tree } StringContext_f bindTo { case (c, app@Apply(Select(Apply(_, parts), _), args)) => c.macro_StringInterpolation_f(parts, args, app.pos) } registry diff --git a/src/compiler/scala/tools/reflect/MacroImplementations.scala b/src/compiler/scala/tools/reflect/MacroImplementations.scala index 604bd7cd1a..e1b959cefa 100644 --- a/src/compiler/scala/tools/reflect/MacroImplementations.scala +++ b/src/compiler/scala/tools/reflect/MacroImplementations.scala @@ -57,6 +57,9 @@ abstract class MacroImplementations { ) } + val stdContextTags = new { val tc: c.type = c } with StdContextTags + import stdContextTags._ + def conversionType(ch: Char, arg: Tree): Option[Type] = { ch match { case 'b' | 'B' => @@ -68,11 +71,11 @@ abstract class MacroImplementations { case 'c' | 'C' => checkType(arg, CharTpe, ByteTpe, ShortTpe, IntTpe) case 'd' | 'o' | 'x' | 'X' => - checkType(arg, IntTpe, LongTpe, ByteTpe, ShortTpe, typeOf[BigInt]) + checkType(arg, IntTpe, LongTpe, ByteTpe, ShortTpe, tagOfBigInt.tpe) case 'e' | 'E' | 'g' | 'G' | 'f' | 'a' | 'A' => - checkType(arg, DoubleTpe, FloatTpe, typeOf[BigDecimal]) + checkType(arg, DoubleTpe, FloatTpe, tagOfBigDecimal.tpe) case 't' | 'T' => - checkType(arg, LongTpe, typeOf[java.util.Calendar], typeOf[java.util.Date]) + checkType(arg, LongTpe, tagOfCalendar.tpe, tagOfDate.tpe) case _ => None } } diff --git a/src/compiler/scala/tools/reflect/StdTags.scala b/src/compiler/scala/tools/reflect/StdTags.scala index 18cbf9c4b7..25a42a82cd 100644 --- a/src/compiler/scala/tools/reflect/StdTags.scala +++ b/src/compiler/scala/tools/reflect/StdTags.scala @@ -4,46 +4,55 @@ package reflect import java.lang.{Class => jClass} import scala.reflect.{ClassTag, classTag} import scala.reflect.base.{MirrorOf, TypeCreator, Universe => BaseUniverse} -import scala.reflect.runtime.{universe => ru} // [Eugene++] Before 2.10 is released, I suggest we don't rely on automated type tag generation // sure, it's convenient, but then refactoring reflection / reification becomes a pain // `ClassTag` tags are fine, because they don't need a reifier to be generated -object StdTags { - // root mirror is fine for these guys, since scala-library.jar is guaranteed to be reachable from the root mirror - lazy val tagOfString = ru.TypeTag[String]( - ru.rootMirror, - new TypeCreator { - def apply[U <: BaseUniverse with Singleton](m: MirrorOf[U]): U # Type = { - val u = m.universe - u.definitions.StringClass.asTypeConstructor - } - }) - lazy val tagOfListOfString = ru.TypeTag[List[String]]( - ru.rootMirror, - new TypeCreator { - def apply[U <: BaseUniverse with Singleton](m: MirrorOf[U]): U # Type = { - val u = m.universe - val pre = u.ThisType(m.staticModule("scala.collection.immutable").moduleClass.asInstanceOf[u.Symbol]) - u.TypeRef(pre, u.definitions.ListClass, List(u.definitions.StringClass.asTypeConstructor)) - } - }) +trait StdTags { + val u: BaseUniverse with Singleton + val m: MirrorOf[u.type] - // root mirror is NOT fine for these guys, hence we use the `currentMirror` trick - private val ourClassloader = getClass.getClassLoader - private def tagOfStaticClass[T: ClassTag] = - ru.TypeTag[T]( - ru.runtimeMirror(ourClassloader), + lazy val tagOfListOfString: u.TypeTag[List[String]] = + u.TypeTag[List[String]]( + m, + new TypeCreator { + def apply[U <: BaseUniverse with Singleton](m: MirrorOf[U]): U # Type = { + val u = m.universe + val pre = u.ThisType(m.staticPackage("scala.collection.immutable").moduleClass.asInstanceOf[u.Symbol]) + u.TypeRef(pre, u.definitions.ListClass, List(u.definitions.StringClass.asTypeConstructor)) + } + }) + + private def tagOfStaticClass[T: ClassTag]: u.TypeTag[T] = + u.TypeTag[T]( + m, new TypeCreator { def apply[U <: BaseUniverse with Singleton](m: MirrorOf[U]): U # Type = m.staticClass(classTag[T].runtimeClass.getName).asTypeConstructor.asInstanceOf[U # Type] }) - lazy val tagOfInt = ru.TypeTag.Int + lazy val tagOfInt = u.TypeTag.Int + lazy val tagOfString = tagOfStaticClass[String] lazy val tagOfFile = tagOfStaticClass[scala.tools.nsc.io.File] lazy val tagOfDirectory = tagOfStaticClass[scala.tools.nsc.io.Directory] lazy val tagOfStdReplVals = tagOfStaticClass[scala.tools.nsc.interpreter.StdReplVals] lazy val tagOfIMain = tagOfStaticClass[scala.tools.nsc.interpreter.IMain] lazy val tagOfThrowable = tagOfStaticClass[java.lang.Throwable] lazy val tagOfClassLoader = tagOfStaticClass[java.lang.ClassLoader] + lazy val tagOfBigInt = tagOfStaticClass[BigInt] + lazy val tagOfBigDecimal = tagOfStaticClass[BigDecimal] + lazy val tagOfCalendar = tagOfStaticClass[java.util.Calendar] + lazy val tagOfDate = tagOfStaticClass[java.util.Date] +} + +object StdRuntimeTags extends StdTags { + val u: scala.reflect.runtime.universe.type = scala.reflect.runtime.universe + val m = u.runtimeMirror(getClass.getClassLoader) + // we need getClass.getClassLoader to support the stuff from scala-compiler.jar +} + +abstract class StdContextTags extends StdTags { + val tc: scala.reflect.makro.Context + val u: tc.universe.type = tc.universe + val m = tc.mirror } diff --git a/src/compiler/scala/tools/util/VerifyClass.scala b/src/compiler/scala/tools/util/VerifyClass.scala index 5f636f63fb..e0e089d0b2 100644 --- a/src/compiler/scala/tools/util/VerifyClass.scala +++ b/src/compiler/scala/tools/util/VerifyClass.scala @@ -13,7 +13,8 @@ object VerifyClass { Class.forName(name, true, cl) (name, None) } catch { - case x => (name, Some(x.toString)) + case x: Throwable => // TODO: only catch VerifyError (and related) + ExceptionInInitializationError (for static objects that bomb on classload) + (name, Some(x.toString)) } } diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala index 3a1dc87a6a..46c644bcd6 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala @@ -36,7 +36,7 @@ trait CPSUtils { lazy val MarkerCPSAdaptMinus = rootMirror.getRequiredClass("scala.util.continuations.cpsMinus") lazy val Context = rootMirror.getRequiredClass("scala.util.continuations.ControlContext") - lazy val ModCPS = rootMirror.getRequiredModule("scala.util.continuations") + lazy val ModCPS = rootMirror.getRequiredPackage("scala.util.continuations") lazy val MethShiftUnit = definitions.getMember(ModCPS, cpsNames.shiftUnit) lazy val MethShiftUnit0 = definitions.getMember(ModCPS, cpsNames.shiftUnit0) diff --git a/src/library-aux/scala/AnyRef.scala b/src/library-aux/scala/AnyRef.scala index 1eefb0c806..7d8b9f9e76 100644 --- a/src/library-aux/scala/AnyRef.scala +++ b/src/library-aux/scala/AnyRef.scala @@ -10,6 +10,7 @@ package scala /** Class `AnyRef` is the root class of all ''reference types''. * All types except the value types descend from this class. + * @template */ trait AnyRef extends Any { diff --git a/src/library/scala/annotation/static.scala b/src/library/scala/annotation/static.scala new file mode 100644 index 0000000000..f2955c756c --- /dev/null +++ b/src/library/scala/annotation/static.scala @@ -0,0 +1,20 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2011, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +package scala.annotation + +/** + * An annotation that marks a member in the companion object as static + * and ensures that the compiler generates static fields/methods for it. + * This is important for Java interoperability and performance reasons. + * + * @since 2.10 + */ +final class static extends StaticAnnotation { + // TODO document exact semantics above! +} diff --git a/src/library/scala/collection/immutable/List.scala b/src/library/scala/collection/immutable/List.scala index 6fd8d143ee..74dc385f99 100644 --- a/src/library/scala/collection/immutable/List.scala +++ b/src/library/scala/collection/immutable/List.scala @@ -62,6 +62,7 @@ import java.io._ * section on `Lists` for more information. * * @define coll list + * @define Coll `List` * @define thatinfo the class of the returned collection. In the standard library configuration, * `That` is always `List[B]` because an implicit of type `CanBuildFrom[List, B, That]` * is defined in object `List`. @@ -96,7 +97,7 @@ sealed abstract class List[+A] extends AbstractSeq[A] * * @usecase def ::(x: A): List[A] * @inheritdoc - * + * * Example: * {{{1 :: List(2, 3) = List(2, 3).::(1) = List(1, 2, 3)}}} */ diff --git a/src/library/scala/collection/immutable/StringOps.scala b/src/library/scala/collection/immutable/StringOps.scala index 633821ecea..7e60cc7195 100644 --- a/src/library/scala/collection/immutable/StringOps.scala +++ b/src/library/scala/collection/immutable/StringOps.scala @@ -25,7 +25,7 @@ import mutable.StringBuilder * @param repr the actual representation of this string operations object. * * @since 2.8 - * @define Coll `StringOps` + * @define Coll `String` * @define coll string */ final class StringOps(override val repr: String) extends AnyVal with StringLike[String] { diff --git a/src/library/scala/collection/mutable/ArrayOps.scala b/src/library/scala/collection/mutable/ArrayOps.scala index 7a595f211d..21c2aaaec7 100644 --- a/src/library/scala/collection/mutable/ArrayOps.scala +++ b/src/library/scala/collection/mutable/ArrayOps.scala @@ -30,7 +30,7 @@ import parallel.mutable.ParArray * * @tparam T type of the elements contained in this array. * - * @define Coll `ArrayOps` + * @define Coll `Array` * @define orderDependent * @define orderDependentFold * @define mayNotTerminateInf diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index a6488b602f..a2ef42fac8 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -18,41 +18,6 @@ package object concurrent { type CancellationException = java.util.concurrent.CancellationException type TimeoutException = java.util.concurrent.TimeoutException - @implicitNotFound("Don't call `Awaitable` methods directly, use the `Await` object.") - sealed trait CanAwait - private implicit object AwaitPermission extends CanAwait - - /** - * `Await` is what is used to ensure proper handling of blocking for `Awaitable` instances. - */ - object Await { - /** - * Invokes ready() on the awaitable, properly wrapped by a call to `scala.concurrent.blocking`. - * ready() blocks until the awaitable has completed or the timeout expires. - * - * Throws a TimeoutException if the timeout expires, as that is in the contract of `Awaitable.ready`. - * @param awaitable the `Awaitable` on which `ready` is to be called - * @param atMost the maximum timeout for which to wait - * @return the result of `awaitable.ready` which is defined to be the awaitable itself. - */ - @throws(classOf[TimeoutException]) - def ready[T](awaitable: Awaitable[T], atMost: Duration): awaitable.type = - blocking(awaitable.ready(atMost)) - - /** - * Invokes result() on the awaitable, properly wrapped by a call to `scala.concurrent.blocking`. - * result() blocks until the awaitable has completed or the timeout expires. - * - * Throws a TimeoutException if the timeout expires, or any exception thrown by `Awaitable.result`. - * @param awaitable the `Awaitable` on which `result` is to be called - * @param atMost the maximum timeout for which to wait - * @return the result of `awaitable.result` - */ - @throws(classOf[Exception]) - def result[T](awaitable: Awaitable[T], atMost: Duration): T = - blocking(awaitable.result(atMost)) - } - /** Starts an asynchronous computation and returns a `Future` object with the result of that computation. * * The result becomes available once the asynchronous computation is completed. @@ -85,5 +50,46 @@ package object concurrent { * - TimeoutException - in the case that the blockable object timed out */ @throws(classOf[Exception]) - def blocking[T](body: =>T): T = BlockContext.current.blockOn(body) + def blocking[T](body: =>T): T = BlockContext.current.blockOn(body)(scala.concurrent.AwaitPermission) +} + +package concurrent { + @implicitNotFound("Don't call `Awaitable` methods directly, use the `Await` object.") + sealed trait CanAwait + + /** + * Internal usage only, implementation detail. + */ + private[concurrent] object AwaitPermission extends CanAwait + + /** + * `Await` is what is used to ensure proper handling of blocking for `Awaitable` instances. + */ + object Await { + /** + * Invokes ready() on the awaitable, properly wrapped by a call to `scala.concurrent.blocking`. + * ready() blocks until the awaitable has completed or the timeout expires. + * + * Throws a TimeoutException if the timeout expires, as that is in the contract of `Awaitable.ready`. + * @param awaitable the `Awaitable` on which `ready` is to be called + * @param atMost the maximum timeout for which to wait + * @return the result of `awaitable.ready` which is defined to be the awaitable itself. + */ + @throws(classOf[TimeoutException]) + def ready[T](awaitable: Awaitable[T], atMost: Duration): awaitable.type = + blocking(awaitable.ready(atMost)(AwaitPermission)) + + /** + * Invokes result() on the awaitable, properly wrapped by a call to `scala.concurrent.blocking`. + * result() blocks until the awaitable has completed or the timeout expires. + * + * Throws a TimeoutException if the timeout expires, or any exception thrown by `Awaitable.result`. + * @param awaitable the `Awaitable` on which `result` is to be called + * @param atMost the maximum timeout for which to wait + * @return the result of `awaitable.result` + */ + @throws(classOf[Exception]) + def result[T](awaitable: Awaitable[T], atMost: Duration): T = + blocking(awaitable.result(atMost)(AwaitPermission)) + } } diff --git a/src/library/scala/reflect/base/Base.scala b/src/library/scala/reflect/base/Base.scala index a4e6256f4d..4457a6cf14 100644 --- a/src/library/scala/reflect/base/Base.scala +++ b/src/library/scala/reflect/base/Base.scala @@ -357,6 +357,9 @@ class Base extends Universe { self => def staticModule(fullName: String): ModuleSymbol = mkStatic[ModuleSymbol](fullName) + def staticPackage(fullName: String): ModuleSymbol = + staticModule(fullName) // this toy universe doesn't care about the distinction between packages and modules + private def mkStatic[S <: Symbol : ClassTag](fullName: String): S = cached(fullName) { val point = fullName lastIndexOf '.' diff --git a/src/library/scala/reflect/base/MirrorOf.scala b/src/library/scala/reflect/base/MirrorOf.scala index 03e035cd81..6dc8090eee 100644 --- a/src/library/scala/reflect/base/MirrorOf.scala +++ b/src/library/scala/reflect/base/MirrorOf.scala @@ -15,11 +15,76 @@ abstract class MirrorOf[U <: base.Universe with Singleton] { /** The symbol corresponding to the globally accessible class with the * given fully qualified name `fullName`. + * + * If the name points to a type alias, it's recursively dealiased and its target is returned. + * If you need a symbol that corresponds to the type alias itself, load it directly from the package class: + * + * scala> cm.staticClass("scala.List") + * res0: reflect.runtime.universe.ClassSymbol = class List + * + * scala> res0.fullName + * res1: String = scala.collection.immutable.List + * + * scala> cm.staticPackage("scala") + * res2: reflect.runtime.universe.ModuleSymbol = package scala + * + * scala> res2.moduleClass.typeSignature member newTypeName("List") + * res3: reflect.runtime.universe.Symbol = type List + * + * scala> res3.fullName + * res4: String = scala.List + * + * To be consistent with Scala name resolution rules, in case of ambiguity between + * a package and an object, the object is never been considered. + * + * For example for the following code: + * + * package foo { + * class B + * } + * + * object foo { + * class A + * class B + * } + * + * staticClass("foo.B") will resolve to the symbol corresponding to the class B declared in the package foo, and + * staticClass("foo.A") will throw a MissingRequirementException (which is exactly what scalac would do if this + * fully qualified class name is written inside any package in a Scala program). + * + * In the example above, to load a symbol that corresponds to the class B declared in the object foo, + * use staticModule("foo") to load the module symbol and then navigate typeSignature.members of its moduleClass. */ def staticClass(fullName: String): U#ClassSymbol /** The symbol corresponding to the globally accessible object with the * given fully qualified name `fullName`. + * + * To be consistent with Scala name resolution rules, in case of ambiguity between + * a package and an object, the object is never been considered. + * + * For example for the following code: + * + * package foo { + * object B + * } + * + * object foo { + * object A + * object B + * } + * + * staticModule("foo.B") will resolve to the symbol corresponding to the object B declared in the package foo, and + * staticModule("foo.A") will throw a MissingRequirementException (which is exactly what scalac would do if this + * fully qualified class name is written inside any package in a Scala program). + * + * In the example above, to load a symbol that corresponds to the object B declared in the object foo, + * use staticModule("foo") to load the module symbol and then navigate typeSignature.members of its moduleClass. */ def staticModule(fullName: String): U#ModuleSymbol + + /** The symbol corresponding to a package with the + * given fully qualified name `fullName`. + */ + def staticPackage(fullName: String): U#ModuleSymbol } diff --git a/src/partest/scala/tools/partest/CompilerTest.scala b/src/partest/scala/tools/partest/CompilerTest.scala index 89c1a9c2ca..fec9893099 100644 --- a/src/partest/scala/tools/partest/CompilerTest.scala +++ b/src/partest/scala/tools/partest/CompilerTest.scala @@ -50,7 +50,7 @@ abstract class CompilerTest extends DirectTest { } class SymsInPackage(pkgName: String) { - def pkg = rootMirror.getRequiredModule(pkgName) + def pkg = rootMirror.getRequiredPackage(pkgName) def classes = allMembers(pkg) filter (_.isClass) def modules = allMembers(pkg) filter (_.isModule) def symbols = classes ++ terms filterNot (_ eq NoSymbol) diff --git a/src/partest/scala/tools/partest/DirectTest.scala b/src/partest/scala/tools/partest/DirectTest.scala index 4e7f36bdc9..5b4e1b4b25 100644 --- a/src/partest/scala/tools/partest/DirectTest.scala +++ b/src/partest/scala/tools/partest/DirectTest.scala @@ -38,7 +38,8 @@ abstract class DirectTest extends App { // new compiler def newCompiler(args: String*): Global = { val settings = newSettings((CommandLineParser tokenize extraSettings) ++ args.toList) - new Global(settings) + if (settings.Yrangepos.value) new Global(settings) with interactive.RangePositions + else new Global(settings) } def newSources(sourceCodes: String*) = sourceCodes.toList.zipWithIndex map { case (src, idx) => new BatchSourceFile("newSource" + (idx + 1), src) @@ -69,7 +70,7 @@ abstract class DirectTest extends App { /** Constructor/main body **/ try show() - catch { case t => println(t) ; t.printStackTrace ; sys.exit(1) } + catch { case t => println(t.getMessage) ; t.printStackTrace ; sys.exit(1) } /** Debugger interest only below this line **/ protected def isDebug = (sys.props contains "partest.debug") || (sys.env contains "PARTEST_DEBUG") diff --git a/src/partest/scala/tools/partest/ScaladocModelTest.scala b/src/partest/scala/tools/partest/ScaladocModelTest.scala index de5354d4a0..ffc5e74cc0 100644 --- a/src/partest/scala/tools/partest/ScaladocModelTest.scala +++ b/src/partest/scala/tools/partest/ScaladocModelTest.scala @@ -11,6 +11,8 @@ import scala.tools.nsc._ import scala.tools.nsc.util.CommandLineParser import scala.tools.nsc.doc.{Settings, DocFactory, Universe} import scala.tools.nsc.doc.model._ +import scala.tools.nsc.doc.model.diagram._ +import scala.tools.nsc.doc.model.comment._ import scala.tools.nsc.reporters.ConsoleReporter /** A class for testing scaladoc model generation @@ -102,13 +104,22 @@ abstract class ScaladocModelTest extends DirectTest { class TemplateAccess(tpl: DocTemplateEntity) { def _class(name: String): DocTemplateEntity = getTheFirst(_classes(name), tpl.qualifiedName + ".class(" + name + ")") - def _classes(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case c: Class => c}) + def _classes(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case c: DocTemplateEntity with Class => c}) + + def _classMbr(name: String): MemberTemplateEntity = getTheFirst(_classesMbr(name), tpl.qualifiedName + ".classMember(" + name + ")") + def _classesMbr(name: String): List[MemberTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case c: MemberTemplateEntity if c.isClass => c}) def _trait(name: String): DocTemplateEntity = getTheFirst(_traits(name), tpl.qualifiedName + ".trait(" + name + ")") - def _traits(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case t: Trait => t}) + def _traits(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case t: DocTemplateEntity with Trait => t}) + + def _traitMbr(name: String): MemberTemplateEntity = getTheFirst(_traitsMbr(name), tpl.qualifiedName + ".traitMember(" + name + ")") + def _traitsMbr(name: String): List[MemberTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case t: MemberTemplateEntity if t.isTrait => t}) def _object(name: String): DocTemplateEntity = getTheFirst(_objects(name), tpl.qualifiedName + ".object(" + name + ")") - def _objects(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case o: Object => o}) + def _objects(name: String): List[DocTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case o: DocTemplateEntity with Object => o}) + + def _objectMbr(name: String): MemberTemplateEntity = getTheFirst(_objectsMbr(name), tpl.qualifiedName + ".objectMember(" + name + ")") + def _objectsMbr(name: String): List[MemberTemplateEntity] = tpl.templates.filter(_.name == name).collect({ case o: MemberTemplateEntity if o.isObject => o}) def _method(name: String): Def = getTheFirst(_methods(name), tpl.qualifiedName + ".method(" + name + ")") def _methods(name: String): List[Def] = tpl.methods.filter(_.name == name) @@ -118,6 +129,18 @@ abstract class ScaladocModelTest extends DirectTest { def _conversion(name: String): ImplicitConversion = getTheFirst(_conversions(name), tpl.qualifiedName + ".conversion(" + name + ")") def _conversions(name: String): List[ImplicitConversion] = tpl.conversions.filter(_.conversionQualifiedName == name) + + def _absType(name: String): MemberEntity = getTheFirst(_absTypes(name), tpl.qualifiedName + ".abstractType(" + name + ")") + def _absTypes(name: String): List[MemberEntity] = tpl.members.filter(mbr => mbr.name == name && mbr.isAbstractType) + + def _absTypeTpl(name: String): DocTemplateEntity = getTheFirst(_absTypeTpls(name), tpl.qualifiedName + ".abstractType(" + name + ")") + def _absTypeTpls(name: String): List[DocTemplateEntity] = tpl.members.collect({ case dtpl: DocTemplateEntity with AbstractType if dtpl.name == name => dtpl }) + + def _aliasType(name: String): MemberEntity = getTheFirst(_aliasTypes(name), tpl.qualifiedName + ".aliasType(" + name + ")") + def _aliasTypes(name: String): List[MemberEntity] = tpl.members.filter(mbr => mbr.name == name && mbr.isAliasType) + + def _aliasTypeTpl(name: String): DocTemplateEntity = getTheFirst(_aliasTypeTpls(name), tpl.qualifiedName + ".aliasType(" + name + ")") + def _aliasTypeTpls(name: String): List[DocTemplateEntity] = tpl.members.collect({ case dtpl: DocTemplateEntity with AliasType if dtpl.name == name => dtpl }) } class PackageAccess(pack: Package) extends TemplateAccess(pack) { @@ -140,7 +163,43 @@ abstract class ScaladocModelTest extends DirectTest { case 1 => list.head case 0 => sys.error("Error getting " + expl + ": No such element.") case _ => sys.error("Error getting " + expl + ": " + list.length + " elements with this name. " + - "All elements in list: [" + list.mkString(", ") + "]") + "All elements in list: [" + list.map({ + case ent: Entity => ent.kind + " " + ent.qualifiedName + case other => other.toString + }).mkString(", ") + "]") + } + + def extractCommentText(c: Any) = { + def extractText(body: Any): String = body match { + case s: String => s + case s: Seq[_] => s.toList.map(extractText(_)).mkString + case p: Product => p.productIterator.toList.map(extractText(_)).mkString + case _ => "" + } + c match { + case c: Comment => + extractText(c.body) + case b: Body => + extractText(b) + } + } + + def countLinks(c: Comment, p: EntityLink => Boolean) = { + def countLinks(body: Any): Int = body match { + case el: EntityLink if p(el) => 1 + case s: Seq[_] => s.toList.map(countLinks(_)).sum + case p: Product => p.productIterator.toList.map(countLinks(_)).sum + case _ => 0 + } + countLinks(c.body) + } + + def testDiagram(doc: DocTemplateEntity, diag: Option[Diagram], nodes: Int, edges: Int) = { + assert(diag.isDefined, doc.qualifiedName + " diagram missing") + assert(diag.get.nodes.length == nodes, + doc.qualifiedName + "'s diagram: node count " + diag.get.nodes.length + " == " + nodes) + assert(diag.get.edges.map(_._2.length).sum == edges, + doc.qualifiedName + "'s diagram: edge count " + diag.get.edges.length + " == " + edges) } } } diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index cd243b9df0..d9b63529eb 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -490,7 +490,6 @@ trait Definitions extends api.StandardDefinitions { def MacroContextPrefixType = if (MacroContextClass != NoSymbol) getMemberType(MacroContextClass, tpnme.PrefixType) else NoSymbol def MacroContextUniverse = if (MacroContextClass != NoSymbol) getMemberMethod(MacroContextClass, nme.universe) else NoSymbol def MacroContextMirror = if (MacroContextClass != NoSymbol) getMemberMethod(MacroContextClass, nme.mirror) else NoSymbol - def MacroContextReify = if (MacroContextClass != NoSymbol) getMemberMethod(MacroContextClass, nme.reify) else NoSymbol lazy val MacroImplAnnotation = requiredClass[scala.reflect.makro.internal.macroImpl] lazy val MacroInternalPackage = getPackageObject("scala.reflect.makro.internal") def MacroInternal_materializeClassTag = getMemberMethod(MacroInternalPackage, nme.materializeClassTag) @@ -899,6 +898,7 @@ trait Definitions extends api.StandardDefinitions { lazy val SwitchClass = requiredClass[scala.annotation.switch] lazy val TailrecClass = requiredClass[scala.annotation.tailrec] lazy val VarargsClass = requiredClass[scala.annotation.varargs] + lazy val StaticClass = requiredClass[scala.annotation.static] lazy val uncheckedStableClass = requiredClass[scala.annotation.unchecked.uncheckedStable] lazy val uncheckedVarianceClass = requiredClass[scala.annotation.unchecked.uncheckedVariance] diff --git a/src/reflect/scala/reflect/internal/Mirrors.scala b/src/reflect/scala/reflect/internal/Mirrors.scala index 761b993539..f7561ae274 100644 --- a/src/reflect/scala/reflect/internal/Mirrors.scala +++ b/src/reflect/scala/reflect/internal/Mirrors.scala @@ -36,7 +36,7 @@ trait Mirrors extends api.Mirrors { if (point > 0) getModuleOrClass(path.toTermName, point) else RootClass val name = path subName (point + 1, len) - val sym = owner.info member name + var sym = owner.info member name val result = if (path.isTermName) sym.suchThat(_ hasFlag MODULE) else sym if (result != NoSymbol) result else { @@ -47,23 +47,39 @@ trait Mirrors extends api.Mirrors { } } - protected def mirrorMissingHook(owner: Symbol, name: Name): Symbol = NoSymbol - - protected def symbolTableMissingHook(owner: Symbol, name: Name): Symbol = self.missingHook(owner, name) - - private[scala] def missingHook(owner: Symbol, name: Name): Symbol = mirrorMissingHook(owner, name) orElse symbolTableMissingHook(owner, name) + /** If you're looking for a class, pass a type name. + * If a module, a term name. + * + * Unlike `getModuleOrClass`, this function + * loads unqualified names from the root package. + */ + private def getModuleOrClass(path: Name): Symbol = + getModuleOrClass(path, path.length) /** If you're looking for a class, pass a type name. * If a module, a term name. + * + * Unlike `getModuleOrClass`, this function + * loads unqualified names from the empty package. */ - private def getModuleOrClass(path: Name): Symbol = getModuleOrClass(path, path.length) + private def staticModuleOrClass(path: Name): Symbol = { + val isPackageless = path.pos('.') == path.length + if (isPackageless) EmptyPackageClass.info decl path + else getModuleOrClass(path) + } + + protected def mirrorMissingHook(owner: Symbol, name: Name): Symbol = NoSymbol + + protected def universeMissingHook(owner: Symbol, name: Name): Symbol = self.missingHook(owner, name) - override def staticClass(fullName: String): ClassSymbol = getRequiredClass(fullName) + private[scala] def missingHook(owner: Symbol, name: Name): Symbol = mirrorMissingHook(owner, name) orElse universeMissingHook(owner, name) - // todo: get rid of most creation methods and keep just staticClass/Module/Package + // todo: get rid of most the methods here and keep just staticClass/Module/Package - def getClassByName(fullname: Name): ClassSymbol = { - var result = getModuleOrClass(fullname.toTypeName) + /************************ loaders of class symbols ************************/ + + private def ensureClassSymbol(fullname: String, sym: Symbol): ClassSymbol = { + var result = sym while (result.isAliasType) result = result.info.typeSymbol result match { case x: ClassSymbol => x @@ -71,45 +87,50 @@ trait Mirrors extends api.Mirrors { } } - override def staticModule(fullName: String): ModuleSymbol = getRequiredModule(fullName) + @deprecated("Use getClassByName", "2.10.0") + def getClass(fullname: Name): ClassSymbol = + getClassByName(fullname) - def getModule(fullname: Name): ModuleSymbol = - // [Eugene++] should be a ClassCastException instead? - getModuleOrClass(fullname.toTermName) match { - case x: ModuleSymbol => x - case _ => MissingRequirementError.notFound("object " + fullname) - } + def getClassByName(fullname: Name): ClassSymbol = + ensureClassSymbol(fullname.toString, getModuleOrClass(fullname.toTypeName)) - def getPackage(fullname: Name): ModuleSymbol = getModule(fullname) + def getRequiredClass(fullname: String): ClassSymbol = + getClassByName(newTypeNameCached(fullname)) - def getRequiredPackage(fullname: String): ModuleSymbol = - getPackage(newTermNameCached(fullname)) + def requiredClass[T: ClassTag] : ClassSymbol = + getRequiredClass(erasureName[T]) - @deprecated("Use getClassByName", "2.10.0") - def getClass(fullname: Name): ClassSymbol = getClassByName(fullname) + def getClassIfDefined(fullname: String): Symbol = + getClassIfDefined(newTypeNameCached(fullname)) - def getRequiredClass(fullname: String): ClassSymbol = - getClassByName(newTypeNameCached(fullname)) match { - case x: ClassSymbol => x - case _ => MissingRequirementError.notFound("class " + fullname) - } + def getClassIfDefined(fullname: Name): Symbol = + wrapMissing(getClassByName(fullname.toTypeName)) - def getRequiredModule(fullname: String): ModuleSymbol = - getModule(newTermNameCached(fullname)) + /** @inheritdoc + * + * Unlike getClassByName/getRequiredClass this function can also load packageless symbols. + * Compiler might ignore them, but they should be loadable with macros. + */ + override def staticClass(fullname: String): ClassSymbol = + ensureClassSymbol(fullname, staticModuleOrClass(newTypeNameCached(fullname))) - def erasureName[T: ClassTag] : String = { - /** We'd like the String representation to be a valid - * scala type, so we have to decode the jvm's secret language. - */ - def erasureString(clazz: Class[_]): String = { - if (clazz.isArray) "Array[" + erasureString(clazz.getComponentType) + "]" - else clazz.getName + /************************ loaders of module symbols ************************/ + + private def ensureModuleSymbol(fullname: String, sym: Symbol, allowPackages: Boolean): ModuleSymbol = + sym match { + case x: ModuleSymbol if allowPackages || !x.isPackage => x + case _ => MissingRequirementError.notFound("object " + fullname) } - erasureString(classTag[T].runtimeClass) - } - def requiredClass[T: ClassTag] : ClassSymbol = - getRequiredClass(erasureName[T]) + @deprecated("Use getModuleByName", "2.10.0") + def getModule(fullname: Name): ModuleSymbol = + getModuleByName(fullname) + + def getModuleByName(fullname: Name): ModuleSymbol = + ensureModuleSymbol(fullname.toString, getModuleOrClass(fullname.toTermName), allowPackages = true) + + def getRequiredModule(fullname: String): ModuleSymbol = + getModule(newTermNameCached(fullname)) // TODO: What syntax do we think should work here? Say you have an object // like scala.Predef. You can't say requiredModule[scala.Predef] since there's @@ -121,34 +142,60 @@ trait Mirrors extends api.Mirrors { def requiredModule[T: ClassTag] : ModuleSymbol = getRequiredModule(erasureName[T] stripSuffix "$") - def getClassIfDefined(fullname: String): Symbol = - getClassIfDefined(newTypeName(fullname)) - - def getClassIfDefined(fullname: Name): Symbol = - wrapMissing(getClassByName(fullname.toTypeName)) - def getModuleIfDefined(fullname: String): Symbol = - getModuleIfDefined(newTermName(fullname)) + getModuleIfDefined(newTermNameCached(fullname)) def getModuleIfDefined(fullname: Name): Symbol = wrapMissing(getModule(fullname.toTermName)) + /** @inheritdoc + * + * Unlike getModule/getRequiredModule this function can also load packageless symbols. + * Compiler might ignore them, but they should be loadable with macros. + */ + override def staticModule(fullname: String): ModuleSymbol = + ensureModuleSymbol(fullname, staticModuleOrClass(newTermNameCached(fullname)), allowPackages = false) + + /************************ loaders of package symbols ************************/ + + private def ensurePackageSymbol(fullname: String, sym: Symbol, allowModules: Boolean): ModuleSymbol = + sym match { + case x: ModuleSymbol if allowModules || x.isPackage => x + case _ => MissingRequirementError.notFound("package " + fullname) + } + + def getPackage(fullname: Name): ModuleSymbol = + ensurePackageSymbol(fullname.toString, getModuleOrClass(fullname), allowModules = true) + + def getRequiredPackage(fullname: String): ModuleSymbol = + getPackage(newTermNameCached(fullname)) + def getPackageObject(fullname: String): ModuleSymbol = - (getModule(newTermName(fullname)).info member nme.PACKAGE) match { + (getPackage(newTermName(fullname)).info member nme.PACKAGE) match { case x: ModuleSymbol => x case _ => MissingRequirementError.notFound("package object " + fullname) } - def getPackageObjectIfDefined(fullname: String): Symbol = { - val module = getModuleIfDefined(newTermName(fullname)) - if (module == NoSymbol) NoSymbol - else { - val packageObject = module.info member nme.PACKAGE - packageObject match { - case x: ModuleSymbol => x - case _ => NoSymbol - } + def getPackageObjectIfDefined(fullname: String): Symbol = + getPackageObjectIfDefined(newTermNameCached(fullname)) + + def getPackageObjectIfDefined(fullname: Name): Symbol = + wrapMissing(getPackageObject(fullname.toTermName)) + + override def staticPackage(fullname: String): ModuleSymbol = + ensurePackageSymbol(fullname.toString, getModuleOrClass(newTermNameCached(fullname)), allowModules = false) + + /************************ helpers ************************/ + + def erasureName[T: ClassTag] : String = { + /** We'd like the String representation to be a valid + * scala type, so we have to decode the jvm's secret language. + */ + def erasureString(clazz: Class[_]): String = { + if (clazz.isArray) "Array[" + erasureString(clazz.getComponentType) + "]" + else clazz.getName } + erasureString(classTag[T].runtimeClass) } @inline private def wrapMissing(body: => Symbol): Symbol = diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index c8a2424118..75962ff9d0 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -246,6 +246,7 @@ trait StdNames { final val BeanPropertyAnnot: NameType = "BeanProperty" final val BooleanBeanPropertyAnnot: NameType = "BooleanBeanProperty" final val bridgeAnnot: NameType = "bridge" + final val staticAnnot: NameType = "static" // Classfile Attributes final val AnnotationDefaultATTR: NameType = "AnnotationDefault" @@ -754,6 +755,7 @@ trait StdNames { val splice: NameType = "splice" val staticClass : NameType = "staticClass" val staticModule : NameType = "staticModule" + val staticPackage : NameType = "staticPackage" val synchronized_ : NameType = "synchronized" val tail: NameType = "tail" val `then` : NameType = "then" diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index d484617767..10d02376b1 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -933,6 +933,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => || hasAnnotation(SerializableAttr) // last part can be removed, @serializable annotation is deprecated ) def hasBridgeAnnotation = hasAnnotation(BridgeClass) + def hasStaticAnnotation = hasAnnotation(StaticClass) def isDeprecated = hasAnnotation(DeprecatedAttr) def deprecationMessage = getAnnotation(DeprecatedAttr) flatMap (_ stringArg 0) def deprecationVersion = getAnnotation(DeprecatedAttr) flatMap (_ stringArg 1) diff --git a/src/reflect/scala/reflect/internal/util/SourceFile.scala b/src/reflect/scala/reflect/internal/util/SourceFile.scala index 7c80ddd37d..df4a3336c3 100644 --- a/src/reflect/scala/reflect/internal/util/SourceFile.scala +++ b/src/reflect/scala/reflect/internal/util/SourceFile.scala @@ -102,17 +102,21 @@ class ScriptSourceFile(underlying: BatchSourceFile, content: Array[Char], overri } /** a file whose contents do not change over time */ -class BatchSourceFile(val file : AbstractFile, val content: Array[Char]) extends SourceFile { - +class BatchSourceFile(val file : AbstractFile, val content0: Array[Char]) extends SourceFile { def this(_file: AbstractFile) = this(_file, _file.toCharArray) def this(sourceName: String, cs: Seq[Char]) = this(new VirtualFile(sourceName), cs.toArray) def this(file: AbstractFile, cs: Seq[Char]) = this(file, cs.toArray) - override def equals(that : Any) = that match { - case that : BatchSourceFile => file.path == that.file.path && start == that.start - case _ => false - } - override def hashCode = file.path.## + start.## + // If non-whitespace tokens run all the way up to EOF, + // positions go wrong because the correct end of the last + // token cannot be used as an index into the char array. + // The least painful way to address this was to add a + // newline to the array. + val content = ( + if (content0.length == 0 || !content0.last.isWhitespace) + content0 :+ '\n' + else content0 + ) val length = content.length def start = 0 def isSelfContained = true @@ -158,4 +162,10 @@ class BatchSourceFile(val file : AbstractFile, val content: Array[Char]) extends lastLine = findLine(0, lines.length, lastLine) lastLine } + + override def equals(that : Any) = that match { + case that : BatchSourceFile => file.path == that.file.path && start == that.start + case _ => false + } + override def hashCode = file.path.## + start.## } diff --git a/src/reflect/scala/reflect/makro/Context.scala b/src/reflect/scala/reflect/makro/Context.scala index f9858a063c..f093016a38 100644 --- a/src/reflect/scala/reflect/makro/Context.scala +++ b/src/reflect/scala/reflect/makro/Context.scala @@ -33,8 +33,4 @@ trait Context extends Aliases /** The prefix tree from which the macro is selected */ val prefix: Expr[PrefixType] - - /** Alias to the underlying mirror's reify */ - // implementation is magically hardwired to `scala.reflect.makro.runtime.ContextReifiers` - def reify[T](expr: T): Expr[T] = macro ??? } diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 9f9f79058d..0878801715 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -72,6 +72,18 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym override def complete(sym: Symbol) = sym setInfo new LazyPackageType } + // reflective mirrors can't know the exhaustive list of available packages + // (that's because compiler mirrors are based on directories and reflective mirrors are based on classloaders, + // and unlike directories classloaders might make up stuff on the fly) + // hence we need to be optimistic and create packages out of thin air + // the same thing is done by the `missingHook` below + override def staticPackage(fullname: String): ModuleSymbol = + try super.staticPackage(fullname) + catch { + case _: MissingRequirementError => + makeScalaPackage(fullname) + } + // ----------- Caching ------------------------------------------------------------------ // [Eugene++ to Martin] not weak? why? diff --git a/src/reflect/scala/tools/nsc/io/VirtualFile.scala b/src/reflect/scala/tools/nsc/io/VirtualFile.scala index b9a946598c..805bc04165 100644 --- a/src/reflect/scala/tools/nsc/io/VirtualFile.scala +++ b/src/reflect/scala/tools/nsc/io/VirtualFile.scala @@ -55,7 +55,7 @@ class VirtualFile(val name: String, override val path: String) extends AbstractF } } - def container: AbstractFile = unsupported + def container: AbstractFile = NoAbstractFile /** Is this abstract file a directory? */ def isDirectory: Boolean = false diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index 1209b710b0..43d4c9dc71 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -700,9 +700,18 @@ trait Blocking extends TestBase { } } + def testFQCNForAwaitAPI(): Unit = once { + done => + + assert(classOf[CanAwait].getName == "scala.concurrent.CanAwait") + assert(Await.getClass.getName == "scala.concurrent.Await") + + done() + } + testAwaitSuccess() testAwaitFailure() - + testFQCNForAwaitAPI() } trait BlockContexts extends TestBase { diff --git a/test/files/neg/macro-cyclic.check b/test/files/neg/macro-cyclic.check index 608381e0e8..7978ec64a5 100644 --- a/test/files/neg/macro-cyclic.check +++ b/test/files/neg/macro-cyclic.check @@ -1,4 +1,4 @@ -Impls_Macros_1.scala:5: error: could not find implicit value for parameter e: SourceLocation
- c.reify { implicitly[SourceLocation] }
- ^
-one error found
+Impls_Macros_1.scala:5: error: could not find implicit value for parameter e: SourceLocation + c.universe.reify { implicitly[SourceLocation] } + ^ +one error found diff --git a/test/files/neg/macro-cyclic/Impls_Macros_1.scala b/test/files/neg/macro-cyclic/Impls_Macros_1.scala index 1ea06fc968..2ecdc3416e 100644 --- a/test/files/neg/macro-cyclic/Impls_Macros_1.scala +++ b/test/files/neg/macro-cyclic/Impls_Macros_1.scala @@ -2,7 +2,7 @@ import scala.reflect.makro.Context object Macros { def impl(c: Context) = { - c.reify { implicitly[SourceLocation] } + c.universe.reify { implicitly[SourceLocation] } } implicit def sourceLocation: SourceLocation1 = macro impl diff --git a/test/files/neg/static-annot.check b/test/files/neg/static-annot.check new file mode 100644 index 0000000000..66efebdcee --- /dev/null +++ b/test/files/neg/static-annot.check @@ -0,0 +1,19 @@ +static-annot.scala:8: error: Only members of top-level objects and their nested objects can be annotated with @static. + @static val bar = 1 + ^ +static-annot.scala:27: error: @static annotated field bar has the same name as a member of class Conflicting + @static val bar = 1 + ^ +static-annot.scala:37: error: The @static annotation is only allowed on public members. + @static private val bar = 1 + ^ +static-annot.scala:38: error: The @static annotation is only allowed on public members. + @static private val baz = 2 + ^ +static-annot.scala:39: error: The @static annotation is not allowed on lazy members. + @static lazy val bam = 3 + ^ +static-annot.scala:14: error: Only members of top-level objects and their nested objects can be annotated with @static. + @static val blah = 2 + ^ +6 errors found
\ No newline at end of file diff --git a/test/files/neg/static-annot.scala b/test/files/neg/static-annot.scala new file mode 100644 index 0000000000..c6c626d42b --- /dev/null +++ b/test/files/neg/static-annot.scala @@ -0,0 +1,47 @@ + + +import annotation.static + + + +class StaticInClass { + @static val bar = 1 +} + + +class NestedObjectInClass { + object Nested { + @static val blah = 2 + } +} + + +object NestedObjectInObject { + object Nested { + @static val succeed = 3 + } +} + + +object Conflicting { + @static val bar = 1 +} + + +class Conflicting { + val bar = 45 +} + + +object PrivateProtectedLazy { + @static private val bar = 1 + @static private val baz = 2 + @static lazy val bam = 3 +} + + +class PrivateProtectedLazy { + println(PrivateProtectedLazy.bar) + println(PrivateProtectedLazy.baz) + println(PrivateProtectedLazy.bam) +} diff --git a/test/files/neg/t4069.check b/test/files/neg/t4069.check index 91bf882cec..08e937bdfe 100644 --- a/test/files/neg/t4069.check +++ b/test/files/neg/t4069.check @@ -12,5 +12,5 @@ t4069.scala:4: error: I encountered a '}' where I didn't expect one, maybe this ^ t4069.scala:10: error: '}' expected but eof found. } -^ + ^ 5 errors found diff --git a/test/files/neg/t4584.check b/test/files/neg/t4584.check index 060160d76a..419f5704b1 100644 --- a/test/files/neg/t4584.check +++ b/test/files/neg/t4584.check @@ -1,4 +1,7 @@ -t4584.scala:1: error: incomplete unicode escape +t4584.scala:1: error: error in unicode escape +class A { val /u2 + ^ +t4584.scala:1: error: illegal character '/uffff' class A { val /u2 ^ -one error found +two errors found diff --git a/test/files/neg/t5856.check b/test/files/neg/t5856.check new file mode 100644 index 0000000000..ac49d4b9ac --- /dev/null +++ b/test/files/neg/t5856.check @@ -0,0 +1,31 @@ +t5856.scala:10: error: invalid string interpolation: `$$', `$'ident or `$'BlockExpr expected + val s9 = s"$" + ^ +t5856.scala:10: error: unclosed string literal + val s9 = s"$" + ^ +t5856.scala:2: error: error in interpolated string: identifier or block expected + val s1 = s"$null" + ^ +t5856.scala:3: error: error in interpolated string: identifier or block expected + val s2 = s"$false" + ^ +t5856.scala:4: error: error in interpolated string: identifier or block expected + val s3 = s"$true" + ^ +t5856.scala:5: error: error in interpolated string: identifier or block expected + val s4 = s"$yield" + ^ +t5856.scala:6: error: error in interpolated string: identifier or block expected + val s5 = s"$return" + ^ +t5856.scala:7: error: error in interpolated string: identifier or block expected + val s6 = s"$new" + ^ +t5856.scala:8: error: error in interpolated string: identifier or block expected + val s7 = s"$s1 $null $super" + ^ +t5856.scala:9: error: error in interpolated string: identifier or block expected + val s8 = s"$super" + ^ +10 errors found diff --git a/test/files/neg/t5856.scala b/test/files/neg/t5856.scala new file mode 100644 index 0000000000..2ceee590af --- /dev/null +++ b/test/files/neg/t5856.scala @@ -0,0 +1,11 @@ +object Test { + val s1 = s"$null" + val s2 = s"$false" + val s3 = s"$true" + val s4 = s"$yield" + val s5 = s"$return" + val s6 = s"$new" + val s7 = s"$s1 $null $super" + val s8 = s"$super" + val s9 = s"$" +}
\ No newline at end of file diff --git a/test/files/neg/unicode-unterminated-quote.check b/test/files/neg/unicode-unterminated-quote.check index fc5caa6d7e..5085505fb4 100644 --- a/test/files/neg/unicode-unterminated-quote.check +++ b/test/files/neg/unicode-unterminated-quote.check @@ -1,4 +1,7 @@ unicode-unterminated-quote.scala:2: error: unclosed string literal val x = /u0022 ^ -one error found +unicode-unterminated-quote.scala:2: error: '}' expected but eof found. + val x = /u0022 + ^ +two errors found diff --git a/test/files/pos/t5957/T_1.scala b/test/files/pos/t5957/T_1.scala index 1db5a3891f..339dcbf0f0 100644 --- a/test/files/pos/t5957/T_1.scala +++ b/test/files/pos/t5957/T_1.scala @@ -1,6 +1,8 @@ abstract class T { - def t1: Test$Bar + // see: SI-6109 + // def t1: Test$Bar def t2: Test#Bar - def t3: Test$Baz + // see: SI-6109 + // def t3: Test$Baz def t4: Test.Baz } diff --git a/test/files/run/existentials-in-compiler.scala b/test/files/run/existentials-in-compiler.scala index 1f314aa4e0..c69d1217fd 100644 --- a/test/files/run/existentials-in-compiler.scala +++ b/test/files/run/existentials-in-compiler.scala @@ -72,7 +72,7 @@ package extest { """ def check(source: String, unit: global.CompilationUnit) = { - getRequiredModule("extest").moduleClass.info.decls.toList.filter(_.isType).map(_.initialize).sortBy(_.name.toString) foreach { clazz => + getRequiredPackage("extest").moduleClass.info.decls.toList.filter(_.isType).map(_.initialize).sortBy(_.name.toString) foreach { clazz => afterTyper { clazz.info println(clazz.defString) diff --git a/test/files/run/macro-openmacros/Impls_Macros_1.scala b/test/files/run/macro-openmacros/Impls_Macros_1.scala index ffeccce1e8..6b92834b81 100644 --- a/test/files/run/macro-openmacros/Impls_Macros_1.scala +++ b/test/files/run/macro-openmacros/Impls_Macros_1.scala @@ -16,7 +16,7 @@ object Macros { import c.universe._ val next = if (c.enclosingMacros.length < 3) c.Expr[Unit](Select(Ident(c.mirror.staticModule("Macros")), newTermName("foo"))) else c.literalUnit - c.reify { + c.universe.reify { println(c.literal(normalizePaths(c.enclosingMacros.toString)).splice) next.splice } diff --git a/test/files/run/macro-reify-basic/Macros_1.scala b/test/files/run/macro-reify-basic/Macros_1.scala index 7a43ee58be..8f8598e248 100644 --- a/test/files/run/macro-reify-basic/Macros_1.scala +++ b/test/files/run/macro-reify-basic/Macros_1.scala @@ -4,7 +4,7 @@ object Macros { def foo(s: String) = macro Impls.foo object Impls { - def foo(c: Ctx)(s: c.Expr[String]) = c.reify { + def foo(c: Ctx)(s: c.Expr[String]) = c.universe.reify { println("hello " + s.splice) } } diff --git a/test/files/run/macro-reify-freevars/Macros_1.scala b/test/files/run/macro-reify-freevars/Macros_1.scala index eafc7f9a82..df1473511d 100644 --- a/test/files/run/macro-reify-freevars/Macros_1.scala +++ b/test/files/run/macro-reify-freevars/Macros_1.scala @@ -7,7 +7,7 @@ object QueryableMacros{ : c.Expr[scala.collection.slick.Queryable[S]] = { import c.universe._ val code = EmptyTree - c.reify{ + c.universe.reify{ Queryable.factory[S]( code.asInstanceOf[reflect.runtime.universe.Tree] ) } } diff --git a/test/files/run/macro-reify-nested-a/Impls_Macros_1.scala b/test/files/run/macro-reify-nested-a/Impls_Macros_1.scala index f9a08df90d..b52b962e31 100644 --- a/test/files/run/macro-reify-nested-a/Impls_Macros_1.scala +++ b/test/files/run/macro-reify-nested-a/Impls_Macros_1.scala @@ -30,7 +30,7 @@ object QueryableMacros{ Apply(Select(c.prefix.tree, newTermName( name )), List( projection.tree )) ).asInstanceOf[Tree] ))) - c.reify{ Queryable.factory[S]( foo.splice )} + c.universe.reify{ Queryable.factory[S]( foo.splice )} } def map[T:c.TypeTag, S:c.TypeTag] (c: scala.reflect.makro.Context) diff --git a/test/files/run/macro-reify-nested-b/Impls_Macros_1.scala b/test/files/run/macro-reify-nested-b/Impls_Macros_1.scala index f9a08df90d..b52b962e31 100644 --- a/test/files/run/macro-reify-nested-b/Impls_Macros_1.scala +++ b/test/files/run/macro-reify-nested-b/Impls_Macros_1.scala @@ -30,7 +30,7 @@ object QueryableMacros{ Apply(Select(c.prefix.tree, newTermName( name )), List( projection.tree )) ).asInstanceOf[Tree] ))) - c.reify{ Queryable.factory[S]( foo.splice )} + c.universe.reify{ Queryable.factory[S]( foo.splice )} } def map[T:c.TypeTag, S:c.TypeTag] (c: scala.reflect.makro.Context) diff --git a/test/files/run/macro-reify-ref-to-packageless/Impls_1.scala b/test/files/run/macro-reify-ref-to-packageless/Impls_1.scala index 2f2d05678d..66c0ee1e9b 100644 --- a/test/files/run/macro-reify-ref-to-packageless/Impls_1.scala +++ b/test/files/run/macro-reify-ref-to-packageless/Impls_1.scala @@ -2,5 +2,5 @@ import scala.reflect.makro.{Context => Ctx} object Impls { val `Answer to the Ultimate Question of Life, the Universe, and Everything` = 42 - def foo(c: Ctx) = c.reify { `Answer to the Ultimate Question of Life, the Universe, and Everything` } + def foo(c: Ctx) = c.universe.reify { `Answer to the Ultimate Question of Life, the Universe, and Everything` } } diff --git a/test/files/run/macro-reify-splice-splice/Macros_1.scala b/test/files/run/macro-reify-splice-splice/Macros_1.scala index 4f1b600f63..0de780b5a2 100644 --- a/test/files/run/macro-reify-splice-splice/Macros_1.scala +++ b/test/files/run/macro-reify-splice-splice/Macros_1.scala @@ -4,8 +4,8 @@ object Macros { def foo = macro Impls.foo object Impls { - def foo(c: Ctx) = c.reify { - { c.reify(c.reify("hello world")) }.splice.splice + def foo(c: Ctx) = c.universe.reify { + { c.universe.reify(c.universe.reify("hello world")) }.splice.splice } } }
\ No newline at end of file diff --git a/test/files/run/macro-reify-staticXXX.check b/test/files/run/macro-reify-staticXXX.check new file mode 100644 index 0000000000..2894fa5843 --- /dev/null +++ b/test/files/run/macro-reify-staticXXX.check @@ -0,0 +1,12 @@ +object +class +object > object +object > class +package > object +package > class +object +class +object > object +object > class +package > object +package > class
\ No newline at end of file diff --git a/test/files/run/macro-reify-staticXXX.flags b/test/files/run/macro-reify-staticXXX.flags new file mode 100644 index 0000000000..cd66464f2f --- /dev/null +++ b/test/files/run/macro-reify-staticXXX.flags @@ -0,0 +1 @@ +-language:experimental.macros
\ No newline at end of file diff --git a/test/files/run/macro-reify-staticXXX/Macros_1.scala b/test/files/run/macro-reify-staticXXX/Macros_1.scala new file mode 100644 index 0000000000..b0ce6507f8 --- /dev/null +++ b/test/files/run/macro-reify-staticXXX/Macros_1.scala @@ -0,0 +1,48 @@ +import scala.reflect.makro.Context + +object B { override def toString = "object" } +class C { override def toString = "class" } + +package foo { + object B { override def toString = "package > object" } + class C { override def toString = "package > class" } +} + +object foo { + object B { override def toString = "object > object" } + class C { override def toString = "object > class" } +} + +object packageless { + def impl(c: Context) = { + import c.universe._ + reify { + println(B) + println(new C) + println(foo.B) + println(new foo.C) + println(_root_.foo.B) + println(new _root_.foo.C) + } + } + + def test = macro impl +} + +package packageful { + object Test { + def impl(c: Context) = { + import c.universe._ + reify { + println(B) + println(new C) + println(foo.B) + println(new foo.C) + println(_root_.foo.B) + println(new _root_.foo.C) + } + } + + def test = macro impl + } +} diff --git a/test/files/run/macro-reify-staticXXX/Test_2.scala b/test/files/run/macro-reify-staticXXX/Test_2.scala new file mode 100644 index 0000000000..6e8cc36080 --- /dev/null +++ b/test/files/run/macro-reify-staticXXX/Test_2.scala @@ -0,0 +1,4 @@ +object Test extends App { + packageless.test + packageful.Test.test +} diff --git a/test/files/run/macro-reify-tagful-a/Macros_1.scala b/test/files/run/macro-reify-tagful-a/Macros_1.scala index 63f117220a..32b09bdcdf 100644 --- a/test/files/run/macro-reify-tagful-a/Macros_1.scala +++ b/test/files/run/macro-reify-tagful-a/Macros_1.scala @@ -5,7 +5,7 @@ object Macros { def foo[T](s: T) = macro Impls.foo[T] object Impls { - def foo[T: c.TypeTag](c: Ctx)(s: c.Expr[T]) = c.reify { + def foo[T: c.TypeTag](c: Ctx)(s: c.Expr[T]) = c.universe.reify { List(s.splice) } } diff --git a/test/files/run/macro-reify-tagless-a/Impls_Macros_1.scala b/test/files/run/macro-reify-tagless-a/Impls_Macros_1.scala index 3796ae99cb..77e4b729d3 100644 --- a/test/files/run/macro-reify-tagless-a/Impls_Macros_1.scala +++ b/test/files/run/macro-reify-tagless-a/Impls_Macros_1.scala @@ -4,7 +4,7 @@ object Macros { def foo[T](s: T) = macro Impls.foo[T] object Impls { - def foo[T](c: Ctx)(s: c.Expr[T]) = c.reify { + def foo[T](c: Ctx)(s: c.Expr[T]) = c.universe.reify { List[T](s.splice) } } diff --git a/test/files/run/macro-reify-unreify/Macros_1.scala b/test/files/run/macro-reify-unreify/Macros_1.scala index 14777506d3..3e9033966d 100644 --- a/test/files/run/macro-reify-unreify/Macros_1.scala +++ b/test/files/run/macro-reify-unreify/Macros_1.scala @@ -11,7 +11,7 @@ object Macros { val greeting = c.reifyTree(c.runtimeUniverse, EmptyTree, c.typeCheck(Apply(Select(Literal(Constant("hello ")), newTermName("$plus")), List(c.unreifyTree(world))))) val typedGreeting = c.Expr[String](greeting) - c.reify { + c.universe.reify { println("hello " + s.splice + " = " + typedGreeting.splice) } } diff --git a/test/files/run/macro-settings/Impls_Macros_1.scala b/test/files/run/macro-settings/Impls_Macros_1.scala index 56e28b506c..20dcdc1bd1 100644 --- a/test/files/run/macro-settings/Impls_Macros_1.scala +++ b/test/files/run/macro-settings/Impls_Macros_1.scala @@ -1,7 +1,7 @@ import scala.reflect.makro.Context object Impls { - def impl(c: Context) = c.reify { + def impl(c: Context) = c.universe.reify { println(c.literal(c.settings.toString).splice) } } diff --git a/test/files/run/macro-sip19-revised/Impls_Macros_1.scala b/test/files/run/macro-sip19-revised/Impls_Macros_1.scala index 994421808e..013130d181 100644 --- a/test/files/run/macro-sip19-revised/Impls_Macros_1.scala +++ b/test/files/run/macro-sip19-revised/Impls_Macros_1.scala @@ -11,7 +11,7 @@ object Macros { val fileName = fun.pos.fileInfo.getName val line = fun.pos.line val charOffset = fun.pos.point - c.reify { SourceLocation1(outer.splice, c.literal(fileName).splice, c.literal(line).splice, c.literal(charOffset).splice) } + c.universe.reify { SourceLocation1(outer.splice, c.literal(fileName).splice, c.literal(line).splice, c.literal(charOffset).splice) } } implicit def sourceLocation: SourceLocation1 = macro impl diff --git a/test/files/run/macro-sip19/Impls_Macros_1.scala b/test/files/run/macro-sip19/Impls_Macros_1.scala index c006ceb691..f6636c298c 100644 --- a/test/files/run/macro-sip19/Impls_Macros_1.scala +++ b/test/files/run/macro-sip19/Impls_Macros_1.scala @@ -7,7 +7,7 @@ object Macros { val fileName = fun.pos.fileInfo.getName val line = fun.pos.line val charOffset = fun.pos.point - c.reify { SourceLocation(c.literal(fileName).splice, c.literal(line).splice, c.literal(charOffset).splice) } + c.universe.reify { SourceLocation(c.literal(fileName).splice, c.literal(line).splice, c.literal(charOffset).splice) } } implicit def sourceLocation: SourceLocation = macro impl diff --git a/test/files/run/macro-typecheck-macrosdisabled2.check b/test/files/run/macro-typecheck-macrosdisabled2.check index e440b2c0cb..2160d3800a 100644 --- a/test/files/run/macro-typecheck-macrosdisabled2.check +++ b/test/files/run/macro-typecheck-macrosdisabled2.check @@ -15,7 +15,7 @@ private def applyImpl[U >: Nothing <: scala.reflect.api.Universe with Singleton]($m$untyped: scala.reflect.base.MirrorOf[U]): scala.reflect.base.Universe#Tree = {
val $u: U = $m$untyped.universe;
val $m: $u.Mirror = $m$untyped.asInstanceOf[$u.Mirror];
- $u.Apply.apply($u.Select.apply($u.Select.apply($u.build.Ident($m.staticModule("scala")), $u.newTermName("Array")), $u.newTermName("apply")), scala.collection.immutable.List.apply[$u.Literal]($u.Literal.apply($u.Constant.apply(2))))
+ $u.Apply.apply($u.Select.apply($u.Select.apply($u.build.Ident($m.staticPackage("scala")), $u.newTermName("Array")), $u.newTermName("apply")), scala.collection.immutable.List.apply[$u.Literal]($u.Literal.apply($u.Constant.apply(2))))
}
};
new $treecreator1()
@@ -28,7 +28,7 @@ def apply[U >: Nothing <: scala.reflect.base.Universe with Singleton]($m$untyped: scala.reflect.base.MirrorOf[U]): U#Type = {
val $u: U = $m$untyped.universe;
val $m: $u.Mirror = $m$untyped.asInstanceOf[$u.Mirror];
- $u.TypeRef.apply($u.ThisType.apply($m.staticModule("scala").asModuleSymbol.moduleClass), $m.staticClass("scala.Array"), scala.collection.immutable.List.apply[$u.Type]($m.staticClass("scala.Int").asTypeSymbol.asTypeConstructor))
+ $u.TypeRef.apply($u.ThisType.apply($m.staticPackage("scala").asModuleSymbol.moduleClass), $m.staticClass("scala.Array"), scala.collection.immutable.List.apply[$u.Type]($m.staticClass("scala.Int").asTypeSymbol.asTypeConstructor))
}
};
new $typecreator2()
diff --git a/test/files/run/macro-undetparams-consfromsls/Impls_Macros_1.scala b/test/files/run/macro-undetparams-consfromsls/Impls_Macros_1.scala index b9bb2cfcca..7b921c0e57 100644 --- a/test/files/run/macro-undetparams-consfromsls/Impls_Macros_1.scala +++ b/test/files/run/macro-undetparams-consfromsls/Impls_Macros_1.scala @@ -2,12 +2,12 @@ import scala.reflect.runtime.universe._ import scala.reflect.makro.Context object Macros { - def cons_impl[A: c.AbsTypeTag](c: Context)(x: c.Expr[A], xs: c.Expr[List[A]]): c.Expr[List[A]] = c.reify { + def cons_impl[A: c.AbsTypeTag](c: Context)(x: c.Expr[A], xs: c.Expr[List[A]]): c.Expr[List[A]] = c.universe.reify { println("A = " + c.literal(implicitly[c.AbsTypeTag[A]].toString).splice) x.splice :: xs.splice } - def nil_impl[B: c.AbsTypeTag](c: Context): c.Expr[List[B]] = c.reify { + def nil_impl[B: c.AbsTypeTag](c: Context): c.Expr[List[B]] = c.universe.reify { println("B = " + c.literal(implicitly[c.AbsTypeTag[B]].toString).splice) Nil } diff --git a/test/files/run/macro-undetparams-macroitself/Impls_Macros_1.scala b/test/files/run/macro-undetparams-macroitself/Impls_Macros_1.scala index 9b1dd8e017..fdba40623b 100644 --- a/test/files/run/macro-undetparams-macroitself/Impls_Macros_1.scala +++ b/test/files/run/macro-undetparams-macroitself/Impls_Macros_1.scala @@ -2,7 +2,7 @@ import scala.reflect.runtime.universe._ import scala.reflect.makro.Context object Macros { - def impl[T: c.TypeTag](c: Context)(foo: c.Expr[T]): c.Expr[Unit] = c.reify { println(c.literal(implicitly[c.TypeTag[T]].toString).splice) } + def impl[T: c.TypeTag](c: Context)(foo: c.Expr[T]): c.Expr[Unit] = c.universe.reify { println(c.literal(implicitly[c.TypeTag[T]].toString).splice) } def foo[T](foo: T) = macro impl[T] }
\ No newline at end of file diff --git a/test/files/run/outertest.scala b/test/files/run/outertest.scala new file mode 100644 index 0000000000..3cc96afa5b --- /dev/null +++ b/test/files/run/outertest.scala @@ -0,0 +1,26 @@ +// A test for the case where the outer field of class B#J should be eliminated. +// You can verify this by running a javap on B.J +abstract class A { + + abstract class I { + + } + + val foo = "foo" + +} + +class B extends A { + + class J extends I { + val bar = foo + } + +} + +object Test extends App { + + val b = new B + assert((new b.J).bar == b.foo) + +} diff --git a/test/files/run/reify-staticXXX.check b/test/files/run/reify-staticXXX.check new file mode 100644 index 0000000000..5762ec47d6 --- /dev/null +++ b/test/files/run/reify-staticXXX.check @@ -0,0 +1,24 @@ +object
+object
+class
+class
+object > object
+object > object
+object > class
+object > class
+package > object
+package > object
+package > class
+package > class
+object
+object
+class
+class
+object > object
+object > object
+object > class
+object > class
+package > object
+package > object
+package > class
+package > class
diff --git a/test/files/run/reify-staticXXX.scala b/test/files/run/reify-staticXXX.scala new file mode 100644 index 0000000000..dc861f843e --- /dev/null +++ b/test/files/run/reify-staticXXX.scala @@ -0,0 +1,56 @@ +import scala.reflect.runtime.universe._ +import scala.tools.reflect.Eval + +object B { override def toString = "object" } +class C { override def toString = "class" } + +package foo { + object B { override def toString = "package > object" } + class C { override def toString = "package > class" } +} + +object foo { + object B { override def toString = "object > object" } + class C { override def toString = "object > class" } +} + +object packageless { + def test = { + println(B) + println(reify(B).eval) + println(new C) + println(reify(new C).eval) + println(foo.B) + println(reify(foo.B).eval) + println(new foo.C) + println(reify(new foo.C).eval) + println(_root_.foo.B) + println(reify(_root_.foo.B).eval) + println(new _root_.foo.C) + println(reify(new _root_.foo.C).eval) + } +} + +package packageful { + object Test { + def test = { + println(B) + println(reify(B).eval) + println(new C) + println(reify(new C).eval) + println(foo.B) + println(reify(foo.B).eval) + println(new foo.C) + println(reify(new foo.C).eval) + println(_root_.foo.B) + println(reify(_root_.foo.B).eval) + println(new _root_.foo.C) + println(reify(new _root_.foo.C).eval) + } + } +} + +object Test extends App { + packageless.test + packageful.Test.test +} diff --git a/test/files/run/static-annot/field.scala b/test/files/run/static-annot/field.scala new file mode 100644 index 0000000000..a7d8158321 --- /dev/null +++ b/test/files/run/static-annot/field.scala @@ -0,0 +1,243 @@ + + + +import java.lang.reflect.Modifier +import annotation.static +import reflect._ + + + +/* TEST 1 */ + +/* A @static-annotated field in the companion object should yield + * a static field in its companion class. + */ +object Foo { + @static val bar = 17 +} + + +class Foo + + +trait Check { + def checkStatic(cls: Class[_]) { + cls.getDeclaredFields.find(_.getName == "bar") match { + case Some(f) => assert(Modifier.isStatic(f.getModifiers), "no static modifier") + case None => assert(false, "no static field bar in class") + } + } + + def test(): Unit +} + + +object Test1 extends Check { + def test() { + checkStatic(classOf[Foo]) + assert(Foo.bar == 17, "Companion object field should be 17.") + } +} + + +/* TEST 2 */ + +class Foo2 + + +/** The order of declaring the class and its companion is inverted now. */ +object Foo2 { + @static val bar = 199 +} + + +object Test2 extends Check { + def test() { + checkStatic(Class.forName("Foo3")) + assert(Foo3.bar == 1984, "Companion object field should be 1984.") + } +} + + +/* TEST 3 */ + +/** The case where there is no explicit companion class */ +object Foo3 { + @static val bar = 1984 +} + + +object Test3 extends Check { + def test() { + checkStatic(Class.forName("Foo3")) + assert(Foo3.bar == 1984, "Companion object field should be 1984.") + } +} + + +/* TEST 4 */ + +/** We want to be able to generate atomic reference field updaters on the companion object + * so that they are created only once per class declaration, but we want them to actually + * be initialize __in the static initializer of the class itself__. + * This is extremely important, because otherwise the creation of the ARFU fails, since it uses + * trickery to detect the caller and compare it to the owner of the field being modified. + * Previously, this used to be circumvented through the use of Java base classes. A pain. + */ +class ArfuTarget { + @volatile var strfield = ArfuTarget.STR + + def CAS(ov: String, nv: String): Boolean = { + ArfuTarget.arfu.compareAndSet(this, ov, nv) + } +} + + +object ArfuTarget { + @static val arfu = java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater(classOf[ArfuTarget], classOf[String], "strfield") + val STR = "Some string" +} + + +object Test4 extends Check { + def checkArfu() { + val at = new ArfuTarget + assert(at.strfield == ArfuTarget.STR) + at.CAS(ArfuTarget.STR, null) + assert(at.strfield == null) + } + + def test() { + checkArfu() + } +} + + +/* TEST 5 */ + +/** Although our main use-case is to use final static fields, we should be able to use non-final too. + * Here we set the static field of the class by using the setters in the companion object. + * It is legal to do so using the reference to `Foo` directly (in which case the callsites + * are rewritten to access the static field directly), or through an interface `Var` (in + * which case the getter and the setter for `field` access the static field in `Var`). + */ +trait Var { + var field: Int +} + +object VarHolder extends Var { + @static var field = 1 +} + + +object Test5 extends Check { + def test() { + assert(VarHolder.field == 1) + VarHolder.field = 2 + assert(VarHolder.field == 2) + val vh: Var = VarHolder + vh.field = 3 + assert(vh.field == 3) + } +} + + +/* TEST 6 */ + +/** Here we test flattening the static ctor body and changing the owners of local definitions. */ +object Foo6 { + var companionField = 101 + @static val staticField = { + val intermediate = companionField + 1 + intermediate * 2 + } +} + + +object Test6 extends Check { + def test() { + assert(Foo6.staticField == 204) + } +} + + + +/* TEST 7 */ + +/** Here we test objects nested in top-level objects */ +object Foo7 { + object AndHisFriend { + @static val bar = "string" + } + class AndHisFriend +} + + +object Test7 extends Check { + def test() { + checkStatic(classOf[Foo7.AndHisFriend]) + assert(Foo7.AndHisFriend.bar == "string") + } +} + + + +/* TEST 8 */ + +object Foo8 { + @static val field = 7 + + val function: () => Int = () => { + field + 1 + } + + val anon = new Runnable { + def run() { + assert(field == 7, "runnable asserting field is 7") + } + } + + @static var mutable = 10 + + val mutation: () => Unit = () => { + mutable += 1 + } +} + +object Test8 { + def test() { + assert(Foo8.function() == 8, "function must return 8") + Foo8.anon.run() + assert(Foo8.mutable == 10, "mutable is 10") + Foo8.mutation() + assert(Foo8.mutable == 11, "mutable is 11") + Foo8.mutation() + assert(Foo8.mutable == 12, "mutable is 12") + } +} + + + + +/* main */ + +object Test { + + def main(args: Array[String]) { + Test1.test() + Test2.test() + Test3.test() + Test4.test() + Test5.test() + Test6.test() + Test7.test() + Test8.test() + } + +} + + + + + + diff --git a/test/files/run/t5385.check b/test/files/run/t5385.check new file mode 100644 index 0000000000..1df74fcfb5 --- /dev/null +++ b/test/files/run/t5385.check @@ -0,0 +1,8 @@ +[0:9] class Azz +[0:9] class Bzz +[0:9] class Czz +[0:9] class Dzz +[0:11] class Ezz +[0:11] class Fzz +[0:13] class Gzz +[0:13] class Hzz diff --git a/test/files/run/t5385.scala b/test/files/run/t5385.scala new file mode 100644 index 0000000000..b803897e71 --- /dev/null +++ b/test/files/run/t5385.scala @@ -0,0 +1,16 @@ +import scala.tools.partest._ + +object Test extends CompilerTest { + import global._ + override def extraSettings = super.extraSettings + " -Yrangepos" + override def sources = List( + "class Azz", "class Bzz ", "class Czz ", "class Dzz\n", + "class Ezz{}", "class Fzz{} ", "class Gzz { }", "class Hzz { } " + ) + def check(source: String, unit: CompilationUnit) { + unit.body foreach { + case cdef: ClassDef => println("%-15s class %s".format(cdef.pos.show, cdef.name)) + case _ => + } + } +} diff --git a/test/files/run/t5713/Impls_Macros_1.scala b/test/files/run/t5713/Impls_Macros_1.scala index d16299a0c8..c041d36523 100644 --- a/test/files/run/t5713/Impls_Macros_1.scala +++ b/test/files/run/t5713/Impls_Macros_1.scala @@ -16,13 +16,13 @@ private object LoggerMacros { type LoggerContext = Context { type PrefixType = Logger.type } def error(c: LoggerContext)(message: c.Expr[String]): c.Expr[Unit] = - log(c)(c.reify(Level.Error), message) + log(c)(c.universe.reify(Level.Error), message) private def log(c: LoggerContext)(level: c.Expr[Level.Value], message: c.Expr[String]): c.Expr[Unit] = // was: if (level.splice.id < 4) // TODO Remove hack! if (c.eval(level).id < 4) // TODO Remove hack! - c.reify(()) + c.universe.reify(()) else { - c.reify(println(message.splice)) + c.universe.reify(println(message.splice)) } }
\ No newline at end of file diff --git a/test/files/run/t5856.scala b/test/files/run/t5856.scala new file mode 100644 index 0000000000..d1e9bd6e58 --- /dev/null +++ b/test/files/run/t5856.scala @@ -0,0 +1,10 @@ +object Test extends App { + override def toString = "Test" + + assert(s"$this" == "Test") + assert(s"$this$this" == "TestTest") + assert(s"$this$$" == "Test$") + assert(s"$this.##" == "Test.##") + assert(s"$this.toString" == "Test.toString") + assert(s"$this=THIS" == "Test=THIS") +}
\ No newline at end of file diff --git a/test/files/run/t6104.check b/test/files/run/t6104.check new file mode 100644 index 0000000000..9766475a41 --- /dev/null +++ b/test/files/run/t6104.check @@ -0,0 +1 @@ +ok diff --git a/test/files/run/t6104.scala b/test/files/run/t6104.scala new file mode 100644 index 0000000000..8ab12c7752 --- /dev/null +++ b/test/files/run/t6104.scala @@ -0,0 +1,8 @@ +class A { Self => + val ok = "ok" + this match { + case me@Self => println(me.ok) + } +} + +object Test extends A with App
\ No newline at end of file diff --git a/test/files/run/toolbox_typecheck_macrosdisabled2.check b/test/files/run/toolbox_typecheck_macrosdisabled2.check index 2e1e2696ab..53041e328d 100644 --- a/test/files/run/toolbox_typecheck_macrosdisabled2.check +++ b/test/files/run/toolbox_typecheck_macrosdisabled2.check @@ -15,7 +15,7 @@ private def applyImpl[U <: scala.reflect.api.Universe with Singleton]($m$untyped: scala.reflect.base.MirrorOf[U]): scala.reflect.base.Universe#Tree = {
val $u: U = $m$untyped.universe;
val $m: $u.Mirror = $m$untyped.asInstanceOf[$u.Mirror];
- $u.Apply.apply($u.Select.apply($u.Select.apply($u.build.Ident($m.staticModule("scala")), $u.newTermName("Array")), $u.newTermName("apply")), scala.collection.immutable.List.apply[$u.Literal]($u.Literal.apply($u.Constant.apply(2))))
+ $u.Apply.apply($u.Select.apply($u.Select.apply($u.build.Ident($m.staticPackage("scala")), $u.newTermName("Array")), $u.newTermName("apply")), scala.collection.immutable.List.apply[$u.Literal]($u.Literal.apply($u.Constant.apply(2))))
}
};
new $treecreator1()
@@ -28,7 +28,7 @@ def apply[U <: scala.reflect.base.Universe with Singleton]($m$untyped: scala.reflect.base.MirrorOf[U]): U#Type = {
val $u: U = $m$untyped.universe;
val $m: $u.Mirror = $m$untyped.asInstanceOf[$u.Mirror];
- $u.TypeRef.apply($u.ThisType.apply($m.staticModule("scala").asModuleSymbol.moduleClass), $m.staticClass("scala.Array"), scala.collection.immutable.List.apply[$u.Type]($m.staticClass("scala.Int").asTypeSymbol.asTypeConstructor))
+ $u.TypeRef.apply($u.ThisType.apply($m.staticPackage("scala").asModuleSymbol.moduleClass), $m.staticClass("scala.Array"), scala.collection.immutable.List.apply[$u.Type]($m.staticClass("scala.Int").asTypeSymbol.asTypeConstructor))
}
};
new $typecreator2()
diff --git a/test/pending/run/macro-expand-implicit-macro-defeats-type-inference/Impls_1.scala b/test/pending/run/macro-expand-implicit-macro-defeats-type-inference/Impls_1.scala index 1740f40daf..599ddf5ed9 100644 --- a/test/pending/run/macro-expand-implicit-macro-defeats-type-inference/Impls_1.scala +++ b/test/pending/run/macro-expand-implicit-macro-defeats-type-inference/Impls_1.scala @@ -1,7 +1,7 @@ import scala.reflect.makro.Context object Impls { - def foo[T: c.TypeTag](c: Context): c.Expr[List[T]] = c.reify { + def foo[T: c.TypeTag](c: Context): c.Expr[List[T]] = c.universe.reify { println("openImplicits are: " + c.literal(c.openImplicits.toString).splice) println("enclosingImplicits are: " + c.literal(c.enclosingImplicits.toString).splice) println("typetag is: " + c.literal(c.tag[T].toString).splice) diff --git a/test/pending/run/macro-reify-array/Macros_1.scala b/test/pending/run/macro-reify-array/Macros_1.scala index c1bd4187a6..4b4cb05884 100644 --- a/test/pending/run/macro-reify-array/Macros_1.scala +++ b/test/pending/run/macro-reify-array/Macros_1.scala @@ -4,7 +4,7 @@ object Macros { def foo[T](s: String) = macro Impls.foo[T] object Impls { - def foo[T: c.TypeTag](c: Ctx)(s: c.Expr[T]) = c.reify { + def foo[T: c.TypeTag](c: Ctx)(s: c.Expr[T]) = c.universe.reify { Array(s.splice) } } diff --git a/test/pending/run/macro-reify-tagful-b/Macros_1.scala b/test/pending/run/macro-reify-tagful-b/Macros_1.scala index 04cf46d3a5..f730bb51f1 100644 --- a/test/pending/run/macro-reify-tagful-b/Macros_1.scala +++ b/test/pending/run/macro-reify-tagful-b/Macros_1.scala @@ -4,7 +4,7 @@ object Macros { def foo[T](s: T) = macro Impls.foo[List[T]] object Impls { - def foo[T: c.TypeTag](c: Ctx)(s: c.Expr[T]) = c.reify { + def foo[T: c.TypeTag](c: Ctx)(s: c.Expr[T]) = c.universe.reify { List(s.splice) } } diff --git a/test/pending/run/macro-reify-tagless-b/Impls_Macros_1.scala b/test/pending/run/macro-reify-tagless-b/Impls_Macros_1.scala index 04366353eb..2c5079ea54 100644 --- a/test/pending/run/macro-reify-tagless-b/Impls_Macros_1.scala +++ b/test/pending/run/macro-reify-tagless-b/Impls_Macros_1.scala @@ -4,7 +4,7 @@ object Macros { def foo[T](s: T) = macro Impls.foo[List[T]] object Impls { - def foo[T](c: Ctx)(s: c.Expr[T]) = c.reify { + def foo[T](c: Ctx)(s: c.Expr[T]) = c.universe.reify { List(s.splice) } } diff --git a/test/pending/run/t5692/Impls_Macros_1.scala b/test/pending/run/t5692/Impls_Macros_1.scala index f9c1e5f12b..7d0e788bd6 100644 --- a/test/pending/run/t5692/Impls_Macros_1.scala +++ b/test/pending/run/t5692/Impls_Macros_1.scala @@ -1,7 +1,7 @@ import scala.reflect.makro.Context object Impls { - def impl[A](c: reflect.makro.Context) = c.reify(()) + def impl[A](c: reflect.makro.Context) = c.universe.reify(()) } object Macros { diff --git a/test/scaladoc/resources/SI-3314-diagrams.scala b/test/scaladoc/resources/SI-3314-diagrams.scala new file mode 100644 index 0000000000..b80a97b522 --- /dev/null +++ b/test/scaladoc/resources/SI-3314-diagrams.scala @@ -0,0 +1,78 @@ +package scala.test.scaladoc { + + /** Check the interaction between SI-3314 and diagrams + * - the three enumerations below should get valid content diagrams: + * Value + * __________/|\__________ + * / / / | \ \ \ + * Mon Tue Wed Thu Fri Sat Sun + * + * - each member should receive an inhertiance diagram: + * Value + * | + * | + * {Mon,Tue,Wed,Thu,Fri,Sat,Sun} + */ + package diagrams { + + /** @contentDiagram + * @inheritanceDiagram hideDiagram */ + trait WeekDayTraitWithDiagram extends Enumeration { + type WeekDay = Value + /** @inheritanceDiagram */ + object Mon extends WeekDay + /** @inheritanceDiagram */ + object Tue extends WeekDay + /** @inheritanceDiagram */ + object Wed extends WeekDay + /** @inheritanceDiagram */ + object Thu extends WeekDay + /** @inheritanceDiagram */ + object Fri extends WeekDay + /** @inheritanceDiagram */ + object Sat extends WeekDay + /** @inheritanceDiagram */ + object Sun extends WeekDay + } + + /** @contentDiagram + * @inheritanceDiagram hideDiagram */ + class WeekDayClassWithDiagram extends Enumeration { + type WeekDay = Value + /** @inheritanceDiagram */ + object Mon extends WeekDay + /** @inheritanceDiagram */ + object Tue extends WeekDay + /** @inheritanceDiagram */ + object Wed extends WeekDay + /** @inheritanceDiagram */ + object Thu extends WeekDay + /** @inheritanceDiagram */ + object Fri extends WeekDay + /** @inheritanceDiagram */ + object Sat extends WeekDay + /** @inheritanceDiagram */ + object Sun extends WeekDay + } + + /** @contentDiagram + * @inheritanceDiagram hideDiagram */ + object WeekDayObjectWithDiagram extends Enumeration { + type WeekDay = Value + /** @inheritanceDiagram */ + object Mon extends WeekDay + /** @inheritanceDiagram */ + object Tue extends WeekDay + /** @inheritanceDiagram */ + object Wed extends WeekDay + /** @inheritanceDiagram */ + object Thu extends WeekDay + /** @inheritanceDiagram */ + object Fri extends WeekDay + /** @inheritanceDiagram */ + object Sat extends WeekDay + /** @inheritanceDiagram */ + object Sun extends WeekDay + } + } +}
\ No newline at end of file diff --git a/test/scaladoc/resources/SI-3314.scala b/test/scaladoc/resources/SI-3314.scala new file mode 100644 index 0000000000..9e0afdce9d --- /dev/null +++ b/test/scaladoc/resources/SI-3314.scala @@ -0,0 +1,85 @@ +package scala.test.scaladoc { + + // testing inherited <documented> templates (Enum.Value is included in the source, thus is documented in scaladoc) + package test1 { + class Enum { + abstract class Value + class Val extends Value + def Value(): Value = new Val + } + + object Constants extends Enum { + def a = Value + } + } + + // testing inherited <not documented> templates (scala.Enumeration.Value is taken from the library, thus is not + // documented in the scaladoc pages -- but should be inherited to make things clear!) + package test2 { + trait WeekDayTrait extends Enumeration { + type WeekDay = Value + val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + } + + class WeekDayClass extends Enumeration { + type WeekDay = Value + val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + } + + object WeekDayObject extends Enumeration { + type WeekDay = Value + val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value + } + + object UserObject { + def isWorkingDay1(d: scala.test.scaladoc.test2.WeekDayClass#Value) = false + def isWorkingDay2(d: scala.test.scaladoc.test2.WeekDayClass#WeekDay) = false + def isWorkingDay3(d: scala.test.scaladoc.test2.WeekDayTrait#Value) = false + def isWorkingDay4(d: scala.test.scaladoc.test2.WeekDayTrait#WeekDay) = false + def isWorkingDay5(d: scala.test.scaladoc.test2.WeekDayObject.Value) = false + def isWorkingDay6(d: scala.test.scaladoc.test2.WeekDayObject.WeekDay) = false + import WeekDayObject._ + def isWorkingDay7(d: Value) = ! (d == Sat || d == Sun) + def isWorkingDay8(d: WeekDay) = ! (d == Sat || d == Sun) + def isWorkingDay9(d: WeekDayObject.Value) = ! (d == Sat || d == Sun) + } + + class UserClass { + def isWorkingDay1(d: scala.test.scaladoc.test2.WeekDayClass#Value) = false + def isWorkingDay2(d: scala.test.scaladoc.test2.WeekDayClass#WeekDay) = false + def isWorkingDay3(d: scala.test.scaladoc.test2.WeekDayTrait#Value) = false + def isWorkingDay4(d: scala.test.scaladoc.test2.WeekDayTrait#WeekDay) = false + def isWorkingDay5(d: scala.test.scaladoc.test2.WeekDayObject.Value) = false + def isWorkingDay6(d: scala.test.scaladoc.test2.WeekDayObject.WeekDay) = false + import WeekDayObject._ + def isWorkingDay7(d: Value) = ! (d == Sat || d == Sun) + def isWorkingDay8(d: WeekDay) = ! (d == Sat || d == Sun) + def isWorkingDay9(d: WeekDayObject.Value) = ! (d == Sat || d == Sun) + } + + trait UserTrait { + def isWorkingDay1(d: scala.test.scaladoc.test2.WeekDayClass#Value) = false + def isWorkingDay2(d: scala.test.scaladoc.test2.WeekDayClass#WeekDay) = false + def isWorkingDay3(d: scala.test.scaladoc.test2.WeekDayTrait#Value) = false + def isWorkingDay4(d: scala.test.scaladoc.test2.WeekDayTrait#WeekDay) = false + def isWorkingDay5(d: scala.test.scaladoc.test2.WeekDayObject.Value) = false + def isWorkingDay6(d: scala.test.scaladoc.test2.WeekDayObject.WeekDay) = false + import WeekDayObject._ + def isWorkingDay7(d: Value) = ! (d == Sat || d == Sun) + def isWorkingDay8(d: WeekDay) = ! (d == Sat || d == Sun) + def isWorkingDay9(d: WeekDayObject.Value) = ! (d == Sat || d == Sun) + } + } + + // testing type lambdas and useless prefixes (should be referenced as T instead of foo.T in the first example) + package test3 { + import language.higherKinds + object `package` { + trait T + trait A + trait X + def foo[T](x: T) = 7 + def bar[A](x: ({type Lambda[X] <: Either[A, X]})#Lambda[String]) = 5 + } + } +} diff --git a/test/scaladoc/resources/SI-4360.scala b/test/scaladoc/resources/SI-4360.scala new file mode 100644 index 0000000000..8e8b96afd5 --- /dev/null +++ b/test/scaladoc/resources/SI-4360.scala @@ -0,0 +1,42 @@ +package scala.test.scaladoc.prefix { + package pack1 { + + class A { + class Z + } + + class B extends A + + package a { + class C + } + + package b { + class C + } + + package c { + class C + + class L extends pack2.Z + + class TEST { + // test inherited classes + def fooCA(x: pack1.A#Z) = 1 + def fooCB(x: pack1.B#Z) = 1 + def fooCS(x: pack2.Z#Z) = 1 + def fooCL(x: L#Z) = 1 + // test in packages + def fooPA(x: pack1.a.C) = 1 + def fooPB(x: pack1.b.C) = 1 + def fooPC(x: pack1.c.C) = 1 + } + + class A extends pack1.A + } + } + + package pack2 { + class Z extends pack1.A + } +}
\ No newline at end of file diff --git a/test/scaladoc/resources/SI-5558.scala b/test/scaladoc/resources/SI-5558.scala new file mode 100644 index 0000000000..6523438b86 --- /dev/null +++ b/test/scaladoc/resources/SI-5558.scala @@ -0,0 +1,6 @@ +package test { + class T + object `package` { + def foo = ??? + } +} diff --git a/test/scaladoc/resources/SI-5784.scala b/test/scaladoc/resources/SI-5784.scala new file mode 100644 index 0000000000..3731d4998c --- /dev/null +++ b/test/scaladoc/resources/SI-5784.scala @@ -0,0 +1,28 @@ +package test.templates { + object `package` { + /** @template */ + type String = java.lang.String + val String = new StringCompanion + class StringCompanion { def boo = ??? } + } + + /** @contentDiagram */ + trait Base { + /** @documentable */ + type String = test.templates.String + /** @template + * @inheritanceDiagram */ + type T <: Foo + val T: FooExtractor + trait Foo { def foo: Int } + trait FooExtractor { def apply(foo: Int); def unapply(t: Foo): Option[Int] } + } + + /** @contentDiagram */ + trait Api extends Base { + /** @documentable + * @inheritanceDiagram */ + override type T <: FooApi + trait FooApi extends Foo { def bar: String } + } +} diff --git a/test/scaladoc/resources/SI_4676.scala b/test/scaladoc/resources/SI_4676.scala deleted file mode 100644 index 00c0fc7ea9..0000000000 --- a/test/scaladoc/resources/SI_4676.scala +++ /dev/null @@ -1,4 +0,0 @@ -class SI_4676 { - type SS = (String,String) - def x(ss: SS): Int = 3 -} diff --git a/test/scaladoc/resources/Trac3484.scala b/test/scaladoc/resources/Trac3484.scala deleted file mode 100644 index 9656ec268d..0000000000 --- a/test/scaladoc/resources/Trac3484.scala +++ /dev/null @@ -1,27 +0,0 @@ -class cbf[A, B, C] - -/** - * @define Coll Traversable - * @define bfreturn $Coll - */ -class Collection[A] { - /** What map does... - * - * $bfreturn - * @usecase def map[B](f: A => B): $bfreturn[B] - * - */ - def map[B, That](f: A => B)(implicit fact: cbf[Collection[A], B, That]) = - null -} - -/** - * @define b John - * @define a Mister $b - */ -class SR704 { - /** - * Hello $a. - */ - def foo = 123 -} diff --git a/test/scaladoc/resources/doc-root/AnyRef.scala b/test/scaladoc/resources/doc-root/AnyRef.scala index 1eefb0c806..7d8b9f9e76 100644 --- a/test/scaladoc/resources/doc-root/AnyRef.scala +++ b/test/scaladoc/resources/doc-root/AnyRef.scala @@ -10,6 +10,7 @@ package scala /** Class `AnyRef` is the root class of all ''reference types''. * All types except the value types descend from this class. + * @template */ trait AnyRef extends Any { diff --git a/test/scaladoc/resources/implicits-known-type-classes-res.scala b/test/scaladoc/resources/implicits-known-type-classes-res.scala new file mode 100644 index 0000000000..9ad652947d --- /dev/null +++ b/test/scaladoc/resources/implicits-known-type-classes-res.scala @@ -0,0 +1,38 @@ +/** Tests the "known type classes" feature of scaladoc implicits + * if the test fails, please update the correct qualified name of + * the type class in src/compiler/scala/tools/nsc/doc/Settings.scala + * in the knownTypeClasses map. Thank you! */ +package scala.test.scaladoc.implicits.typeclasses { + class A[T] + object A { + import language.implicitConversions + import scala.reflect.{ClassTag, TypeTag} + implicit def convertNumeric [T: Numeric] (a: A[T]) = new B(implicitly[Numeric[T]]) + implicit def convertIntegral [T: Integral] (a: A[T]) = new B(implicitly[Integral[T]]) + implicit def convertFractional [T: Fractional] (a: A[T]) = new B(implicitly[Fractional[T]]) + implicit def convertManifest [T: Manifest] (a: A[T]) = new B(implicitly[Manifest[T]]) + implicit def convertClassManifest [T: ClassManifest] (a: A[T]) = new B(implicitly[ClassManifest[T]]) + implicit def convertOptManifest [T: OptManifest] (a: A[T]) = new B(implicitly[OptManifest[T]]) + implicit def convertClassTag [T: ClassTag] (a: A[T]) = new B(implicitly[ClassTag[T]]) + implicit def convertTypeTag [T: TypeTag] (a: A[T]) = new B(implicitly[TypeTag[T]]) + type K[X] = Numeric[X] + type L[X] = Integral[X] + type M[X] = Fractional[X] + type N[X] = Manifest[X] + type O[X] = ClassManifest[X] + type P[X] = OptManifest[X] + type Q[X] = ClassTag[X] + type R[X] = TypeTag[X] + implicit def convertK [T: K] (a: A[T]) = new B(implicitly[K[T]]) + implicit def convertL [T: L] (a: A[T]) = new B(implicitly[L[T]]) + implicit def convertM [T: M] (a: A[T]) = new B(implicitly[M[T]]) + implicit def convertN [T: N] (a: A[T]) = new B(implicitly[N[T]]) + implicit def convertO [T: O] (a: A[T]) = new B(implicitly[O[T]]) + implicit def convertP [T: P] (a: A[T]) = new B(implicitly[P[T]]) + implicit def convertQ [T: Q] (a: A[T]) = new B(implicitly[Q[T]]) + implicit def convertR [T: R] (a: A[T]) = new B(implicitly[R[T]]) + } + class B[T](t: T) { + def typeClass: T = t + } +}
\ No newline at end of file diff --git a/test/scaladoc/resources/implicits-scopes-res.scala b/test/scaladoc/resources/implicits-scopes-res.scala index aaeb43f95b..c675a645bd 100644 --- a/test/scaladoc/resources/implicits-scopes-res.scala +++ b/test/scaladoc/resources/implicits-scopes-res.scala @@ -22,7 +22,7 @@ package test2 { package classes { class A class B { def b = "" } - object test { /* (new A).b won't compile */ } + object test { (new A).b } } } diff --git a/test/scaladoc/resources/links.scala b/test/scaladoc/resources/links.scala new file mode 100644 index 0000000000..679d0b0dce --- /dev/null +++ b/test/scaladoc/resources/links.scala @@ -0,0 +1,57 @@ +// that would be: +// SI-5079 "Scaladoc can't link to an object (only a class or trait)" +// SI-4497 "Links in ScalaDoc - Spec and implementation unsufficient" +// SI-4224 "Wiki-links should support method targets" +// SI-3695 "support non-fully-qualified type links in scaladoc comments" +package scala.test.scaladoc.links { + import language.higherKinds + class C + + trait Target { + type T + type S = String + class C + def foo(i: Int) = 2 + def foo(s: String) = 3 + def foo[A[_]](x: A[String]) = 5 + def foo[A[_[_]]](x: A[List]) = 6 + val bar: Boolean + def baz(c: scala.test.scaladoc.links.C) = 7 + } + + object Target { + type T = Int => Int + type S = Int + class C + def foo(i: Int) = 2 + def foo(z: String) = 3 + def foo[A[_]](x: A[String]) = 5 + def foo[A[_[_]]](x: A[List]) = 6 + val bar: Boolean = false + val onlyInObject = 1 + def baz(c: scala.test.scaladoc.links.C) = 7 + } + + /** + * Links to the trait: + * - [[scala.test.scaladoc.links.Target!.T trait Target -> type T]] + * - [[test.scaladoc.links.Target!.S trait Target -> type S]] + * - [[scaladoc.links.Target!.foo(Int)* trait Target -> def foo]] + * - [[links.Target!.bar trait Target -> def bar]] + * - [[[[Target!.foo[A[_[_]]]* trait Target -> def foo with 3 nested tparams]]]] (should exercise nested parens) + * - [[Target$.T object Target -> type T]] + * - [[Target$.S object Target -> type S]] + * - [[Target$.foo(Str* object Target -> def foo]] + * - [[Target$.bar object Target -> def bar]] + * - [[[[Target$.foo[A[_[_]]]* trait Target -> def foo with 3 nested tparams]]]] (should exercise nested parens) + * - [[Target.onlyInObject object Target -> def foo]] (should find the object) + * - [[Target$.C object Target -> class C]] (should link directly to C, not as a member) + * - [[Target!.C trait Target -> class C]] (should link directly to C, not as a member) + * - [[Target$.baz(links\.C)* object Target -> def baz]] (should use dots in prefix) + * - [[Target!.baz(links\.C)* trait Target -> def baz]] (should use dots in prefix) + * - [[localMethod object TEST -> localMethod]] (should use the current template to resolve link instead of inTpl, that's the package) + */ + object TEST { + def localMethod = 3 + } +} diff --git a/test/scaladoc/resources/package-object-res.scala b/test/scaladoc/resources/package-object-res.scala index 17d5c0a499..f1f714dd1f 100644 --- a/test/scaladoc/resources/package-object-res.scala +++ b/test/scaladoc/resources/package-object-res.scala @@ -1,4 +1,4 @@ -/** This package have A and B. +/** This package has A and B. */ package test { trait A { def hi = "hello" } diff --git a/test/scaladoc/run/SI-3314-diagrams.check b/test/scaladoc/run/SI-3314-diagrams.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/SI-3314-diagrams.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/SI-3314-diagrams.scala b/test/scaladoc/run/SI-3314-diagrams.scala new file mode 100644 index 0000000000..050c6e7f48 --- /dev/null +++ b/test/scaladoc/run/SI-3314-diagrams.scala @@ -0,0 +1,28 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + override def resourceFile = "SI-3314-diagrams.scala" + + // no need for special settings + def scaladocSettings = "-diagrams" + + def testModel(rootPackage: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + // just need to check the member exists, access methods will throw an error if there's a problem + val base = rootPackage._package("scala")._package("test")._package("scaladoc") + + val diagrams = base._package("diagrams") + val templates = List(diagrams._trait("WeekDayTraitWithDiagram"), diagrams._class("WeekDayClassWithDiagram"), diagrams._object("WeekDayObjectWithDiagram")) + + for (template <- templates) { + testDiagram(template, template.contentDiagram, 8, 7) + val subtemplates = List("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun").map(template._object(_)) + for (subtemplate <- subtemplates) + testDiagram(subtemplate, subtemplate.inheritanceDiagram, 2, 1) + } + } +}
\ No newline at end of file diff --git a/test/scaladoc/run/SI-3314.check b/test/scaladoc/run/SI-3314.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/SI-3314.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/SI-3314.scala b/test/scaladoc/run/SI-3314.scala new file mode 100644 index 0000000000..fe220b08af --- /dev/null +++ b/test/scaladoc/run/SI-3314.scala @@ -0,0 +1,91 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + override def resourceFile = "SI-3314.scala" + + // no need for special settings + def scaladocSettings = "-feature" + + def testModel(rootPackage: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + // just need to check the member exists, access methods will throw an error if there's a problem + val base = rootPackage._package("scala")._package("test")._package("scaladoc") + + + + // test1 + + val test1 = base._package("test1") + val test1Value = test1._class("Enum")._method("Value").resultType + assert(test1Value.name == "Value", test1Value.name + " == Value") + assert(test1Value.refEntity.size == 1, test1Value.refEntity.size + " == 1") + + val test1Constants = test1._object("Constants")._method("a").resultType + assert(test1Constants.name == "Value", test1Constants.name + " == Value") + assert(test1Constants.refEntity.size == 1, test1Constants.refEntity.size + " == 1") + assert(test1Constants.refEntity(0)._1 == LinkToMember(test1._object("Constants")._class("Value"), test1._object("Constants")), + test1Constants.refEntity(0)._1 + " == LinkToMember(test1.Enum.Value)") + + + // test2 + + val test2 = base._package("test2") + def testDefinition(doc: DocTemplateEntity) = { + for (day <- List("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")) { + assert(doc._value(day).resultType.name == "Value", + doc._value(day).resultType.name + " == Value") + assert(doc._value(day).resultType.refEntity.size == 1, + doc._value(day).resultType.refEntity.size + " == 1") + assert(doc._value(day).resultType.refEntity(0)._1 == LinkToMember(doc._classMbr("Value"), doc), + doc._value(day).resultType.refEntity(0)._1 + " == LinkToMember(" + doc.qualifiedName + ".Value)") + } + } + testDefinition(test2._trait("WeekDayTrait")) + testDefinition(test2._class("WeekDayClass")) + testDefinition(test2._object("WeekDayObject")) + + def testUsage(doc: DocTemplateEntity) = { + val ValueInClass = test2._class("WeekDayClass")._classMbr("Value") + val ValueInTrait = test2._trait("WeekDayTrait")._classMbr("Value") + val ValueInObject = test2._object("WeekDayObject")._classMbr("Value") + val WeekDayInObject = test2._object("WeekDayObject")._member("WeekDay") + + val expected = List( + ("isWorkingDay1", "WeekDayClass.Value", ValueInClass), + ("isWorkingDay2", "WeekDayClass.Value", ValueInClass), + ("isWorkingDay3", "WeekDayTrait.Value", ValueInTrait), + ("isWorkingDay4", "WeekDayTrait.Value", ValueInTrait), + ("isWorkingDay5", "WeekDayObject.Value", ValueInObject), + ("isWorkingDay6", "WeekDay", WeekDayInObject), + ("isWorkingDay7", "WeekDayObject.Value", ValueInObject), + ("isWorkingDay8", "WeekDay", WeekDayInObject), + ("isWorkingDay9", "WeekDayObject.Value", ValueInObject)) + + for ((method, name, ref) <- expected) { + assert(doc._method(method).valueParams(0)(0).resultType.name == name, + doc._method(method).valueParams(0)(0).resultType.name + " == " + name + " (in " + doc + "." + method + ")") + assert(doc._method(method).valueParams(0)(0).resultType.refEntity.size == 1, + doc._method(method).valueParams(0)(0).resultType.refEntity.size + " == " + 1 + " (in " + doc + "." + method + ")") + assert(doc._method(method).valueParams(0)(0).resultType.refEntity(0)._1 == LinkToMember(ref, ref.inTemplate), + doc._method(method).valueParams(0)(0).resultType.refEntity(0)._1 + " == LinkToMember(" + ref.qualifiedName + ") (in " + doc + "." + method + ")") + } + } + testUsage(test2._object("UserObject")) + testUsage(test2._class("UserClass")) + testUsage(test2._trait("UserTrait")) + + + // test3 + val test3 = base._package("test3") + val foo = test3._method("foo") + assert(foo.valueParams(0)(0).resultType.name == "T", + foo.valueParams(0)(0).resultType.name + " == T") + val bar = test3._method("bar") + assert(bar.valueParams(0)(0).resultType.name == "(AnyRef { type Lambda[X] <: Either[A,X] })#Lambda[String]", + bar.valueParams(0)(0).resultType.name + " == (AnyRef { type Lambda[X] <: Either[A,X] })#Lambda[String]") + } +}
\ No newline at end of file diff --git a/test/scaladoc/run/SI-3448.check b/test/scaladoc/run/SI-3448.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/SI-3448.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/SI-3448.scala b/test/scaladoc/run/SI-3448.scala new file mode 100644 index 0000000000..a2d3f59596 --- /dev/null +++ b/test/scaladoc/run/SI-3448.scala @@ -0,0 +1,38 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + // Working around the fact that usecases have the form Coll[T] and not Coll[T, U], as required by Map + override def code = """ + /** + * @define Coll C[T] + */ + class C[T] { + /** + * @usecase def foo[T]: $Coll[T] + */ + def foo[T: Numeric]: C[T] + } + + + /** + * @define Coll D1[T] + */ + class D[U, T] extends C[T] { + protected type D1[Z] = D[U, Z] + } + """ + + // no need for special settings + def scaladocSettings = "" + + def testModel(rootPackage: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + // just need to check the member exists, access methods will throw an error if there's a problem + assert(rootPackage._class("D")._method("foo").resultType.name == "D[U, T]", + rootPackage._class("D")._method("foo").resultType.name + " == D[U, T]") + } +}
\ No newline at end of file diff --git a/test/scaladoc/run/SI-3484.check b/test/scaladoc/run/SI-3484.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/SI-3484.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/SI-3484.scala b/test/scaladoc/run/SI-3484.scala new file mode 100644 index 0000000000..297aebee8f --- /dev/null +++ b/test/scaladoc/run/SI-3484.scala @@ -0,0 +1,52 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.nsc.doc.model.diagram._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + override def code = """ + class cbf[A, B, C] + + /** + * @define Coll Collection + * @define bfreturn $Coll + */ + class Collection[A] { + /** What map does... + * + * $bfreturn + * @usecase def map[B](f: A => B): $bfreturn[B] + * + */ + def map[B, That](f: A => B)(implicit fact: cbf[Collection[A], B, That]) = + null + } + + /** + * @define b John + * @define a Mister $b + */ + class SR704 { + /** + * Hello $a. + */ + def foo = 123 + } + """ + + // diagrams must be started. In case there's an error with dot, it should not report anything + def scaladocSettings = "" + + def testModel(rootPackage: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + // check correct expansion of the use case signature + val map = rootPackage._class("Collection")._method("map") + assert(map.resultType.name == "Collection[B]", map.resultType.name + " == Traversable[B]") + + val foo = rootPackage._class("SR704")._method("foo") + assert(extractCommentText(foo.comment.get).contains("Hello Mister John."), + extractCommentText(foo.comment.get) + ".contains(Hello Mister John.)") + } +}
\ No newline at end of file diff --git a/test/scaladoc/run/SI-4324.check b/test/scaladoc/run/SI-4324.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/SI-4324.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/SI-4324.scala b/test/scaladoc/run/SI-4324.scala new file mode 100644 index 0000000000..686a133dc0 --- /dev/null +++ b/test/scaladoc/run/SI-4324.scala @@ -0,0 +1,24 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + override def code = """ + case class Test4324(arg11: String, arg12: Int)(arg21: String, arg22: Int)(arg31: Int, arg32: String) + """ + + // no need for special settings + def scaladocSettings = "" + + def testModel(rootPackage: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + // just need to check the member exists, access methods will throw an error if there's a problem + rootPackage._class("Test4324").asInstanceOf[Class].valueParams match { + case List(List(arg11, arg12), List(arg21, arg22), List(arg31, arg32)) => //yeeey, do nothing + case other => + assert(false, "Incorrect valueParams generated: " + other + " instead of (arg11, arg12)(arg21, arg22)(arg31, arg32)") + } + } +}
\ No newline at end of file diff --git a/test/scaladoc/run/SI-4360.check b/test/scaladoc/run/SI-4360.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/SI-4360.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/SI-4360.scala b/test/scaladoc/run/SI-4360.scala new file mode 100644 index 0000000000..3abc61c267 --- /dev/null +++ b/test/scaladoc/run/SI-4360.scala @@ -0,0 +1,48 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + override def resourceFile = "SI-4360.scala" + + // no need for special settings + def scaladocSettings = "" + + def testModel(rootPackage: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + // just need to check the member exists, access methods will throw an error if there's a problem + val base = rootPackage._package("scala")._package("test")._package("scaladoc")._package("prefix") + + val TEST = base._package("pack1")._package("c")._class("TEST") + val fooCA = TEST._method("fooCA") + val fooCB = TEST._method("fooCB") + val fooCS = TEST._method("fooCS") + val fooCL = TEST._method("fooCL") + val fooPA = TEST._method("fooPA") + val fooPB = TEST._method("fooPB") + val fooPC = TEST._method("fooPC") + + val expected = List( + (fooCA, "Z", 1), + (fooCB, "B.Z", 1), + (fooCS, "pack2.Z.Z", 1), + (fooCL, "L.Z", 1), + (fooPA, "a.C", 1), + (fooPB, "b.C", 1), + (fooPC, "C", 1) + ) + + for ((method, name, refs) <- expected) { + assert(method.valueParams(0)(0).resultType.name == name, + method.valueParams(0)(0).resultType.name + " == " + name + " (in " + method.qualifiedName + ")") + assert(method.valueParams(0)(0).resultType.refEntity.size == refs, + method.valueParams(0)(0).resultType.refEntity.size + " == " + refs + " (in " + method.qualifiedName + ")") + } + + val A = base._package("pack1")._package("c")._class("A") + assert(A.linearizationTypes(0).name == "pack1.A", A.linearizationTypes(0).name + " == pack1.A") + assert(A.linearizationTypes(0).refEntity.size == 1, A.linearizationTypes(0).refEntity.size + " == 1") + } +}
\ No newline at end of file diff --git a/test/scaladoc/run/SI-4676.check b/test/scaladoc/run/SI-4676.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/SI-4676.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/SI-4676.scala b/test/scaladoc/run/SI-4676.scala new file mode 100644 index 0000000000..b83a59a472 --- /dev/null +++ b/test/scaladoc/run/SI-4676.scala @@ -0,0 +1,26 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.nsc.doc.model.diagram._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + override def code = """ + class SI_4676 { + type SS = (String,String) + def x(ss: SS): Int = 3 + } + class cbf[A, B, C] + """ + + // diagrams must be started. In case there's an error with dot, it should not report anything + def scaladocSettings = "" + + def testModel(rootPackage: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + // check correct expansion of the use case signature + val x = rootPackage._class("SI_4676")._method("x") + assert(x.valueParams(0)(0).resultType.name == "(String, String)", "parameter ss of method x has type (String, String") + } +}
\ No newline at end of file diff --git a/test/scaladoc/run/SI-4887.check b/test/scaladoc/run/SI-4887.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/SI-4887.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/SI-4887.scala b/test/scaladoc/run/SI-4887.scala new file mode 100644 index 0000000000..af83344613 --- /dev/null +++ b/test/scaladoc/run/SI-4887.scala @@ -0,0 +1,46 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + override def code = """ + package scala.test.scaladoc.existentials { + import language.higherKinds + import language.existentials + + class X[T, U, V] + + trait TEST { + type T + type U + type A + def foo1(x: X[T, U, _]) = 3 + def foo2(x: X[Z[_], U, z.type] forSome {type Z[_] <: { def z: String }; val z: Z[_ <: Int]}) = 4 + def foo3(x: X[Z, Z, V] forSome { type Z <: T; type V <: T }) = 6 + } + } + """ + + // no need for special settings + def scaladocSettings = "-feature" + + def testModel(rootPackage: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + val base = rootPackage._package("scala")._package("test")._package("scaladoc")._package("existentials") + val TEST = base._trait("TEST") + + val foo1 = TEST._method("foo1") + assert(foo1.valueParams(0)(0).resultType.name == "X[T, U, _]", + foo1.valueParams(0)(0).resultType.name + " == X[T, U, _]") + + val foo2 = TEST._method("foo2") + assert(foo2.valueParams(0)(0).resultType.name == "X[Z[_], U, _ <: [_]AnyRef { def z: String } with Singleton]", + foo2.valueParams(0)(0).resultType.name + " == X[Z[_], U, _ <: [_]AnyRef { def z: String } with Singleton]") + + val foo3 = TEST._method("foo3") + assert(foo3.valueParams(0)(0).resultType.name == "X[Z, Z, V] forSome {type Z <: T, type V <: T}", + foo3.valueParams(0)(0).resultType.name + " == X[Z, Z, V] forSome {type Z <: T, type V <: T}") + } +}
\ No newline at end of file diff --git a/test/scaladoc/run/SI-5235.check b/test/scaladoc/run/SI-5235.check new file mode 100644 index 0000000000..d9acfd063b --- /dev/null +++ b/test/scaladoc/run/SI-5235.check @@ -0,0 +1,4 @@ +newSource:10: warning: Could not find the type $Coll points to while expanding it for the usecase signature of method reverse in trait SpecificColl.In this context, $Coll = "BullSh". + * @usecase def reverse(): $Coll + ^ +Done. diff --git a/test/scaladoc/run/SI-5235.scala b/test/scaladoc/run/SI-5235.scala new file mode 100644 index 0000000000..6295fc7786 --- /dev/null +++ b/test/scaladoc/run/SI-5235.scala @@ -0,0 +1,87 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.nsc.doc.model.diagram._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + override def code = """ + package scala.test.scaladoc.SI5235 { + trait Builder[From, To] + + /** + * @define Coll `GenericColl` + */ + class GenericColl { + /** + * @usecase def reverse(): $Coll + * Returns the reversed $Coll. + */ + def reverse[T](implicit something: Builder[GenericColl, T]): T + def foo1: GenericColl = ??? + } + + /** Nooo, don't point to this */ + trait MyCollection + + package specific { + /** + * @define Coll `BullSh` + */ + trait SpecificColl extends GenericColl { + def foo2: SpecificColl = ??? + } + } + + package mycoll { + /** + * @define Coll `mycoll.MyCollection` + */ + class MyCollection extends specific.SpecificColl { + def foo3: MyCollection = ??? + } + } + } + """ + + // diagrams must be started. In case there's an error with dot, it should not report anything + def scaladocSettings = "" + + def testModel(rootPackage: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + val base = rootPackage._package("scala")._package("test")._package("scaladoc")._package("SI5235") + + val GenericColl = base._class("GenericColl") + val SpecificColl = base._package("specific")._trait("SpecificColl") + val MyCollection = base._package("mycoll")._class("MyCollection") + + // check comment text + val gcComment = extractCommentText(GenericColl._method("reverse").comment.get) + val scComment = extractCommentText(SpecificColl._method("reverse").comment.get) + val mcComment = extractCommentText(MyCollection._method("reverse").comment.get) + assert(gcComment.contains("Returns the reversed GenericColl."), + gcComment + ".contains(\"Returns the reversed GenericColl.\")") + assert(scComment.contains("Returns the reversed BullSh."), + scComment + ".contains(\"Returns the reversed BullSh.\")") + assert(mcComment.contains("Returns the reversed mycoll.MyCollection."), + mcComment + ".contains(\"Returns the reversed mycoll.MyCollection.\")") + + // check signatures + val gcReverse = GenericColl._method("reverse") + val scReverse = SpecificColl._method("reverse") + val mcReverse = MyCollection._method("reverse") + val gcReverseType = gcReverse.resultType + val scReverseType = scReverse.resultType + val mcReverseType = mcReverse.resultType + assert(gcReverseType.name == "GenericColl", gcReverseType.name + " == GenericColl") + assert(scReverseType.name == "BullSh", scReverseType.name + " == BullSh") + assert(mcReverseType.name == "MyCollection",mcReverseType.name + " == MyCollection") + assert(gcReverseType.refEntity(0)._1 == LinkToTpl(GenericColl), + gcReverse.qualifiedName + "'s return type has a link to " + GenericColl.qualifiedName) + assert(scReverseType.refEntity(0)._1 == Tooltip("BullSh"), + scReverseType.refEntity(0)._1 + " == Tooltip(\"BullSh\")") + assert(mcReverseType.refEntity(0)._1 == LinkToTpl(MyCollection), + mcReverse.qualifiedName + "'s return type has a link to " + MyCollection.qualifiedName) + } +}
\ No newline at end of file diff --git a/test/scaladoc/run/SI-5533.check b/test/scaladoc/run/SI-5533.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/SI-5533.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/SI-5533.scala b/test/scaladoc/run/SI-5533.scala new file mode 100644 index 0000000000..989d9aa13a --- /dev/null +++ b/test/scaladoc/run/SI-5533.scala @@ -0,0 +1,39 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + // Working around the fact that usecases have the form Coll[T] and not Coll[T, U], as required by Map + override def code = """ + package a { + class A { class Z } + class C extends b.B { class X extends Y } + } + + package b { + /** @contentDiagram */ + class B extends a.A { class Y extends Z } + /** @contentDiagram */ + class D extends a.C { class V extends X } + } + """ + + // no need for special settings + def scaladocSettings = "-diagrams -skip-packages a" + + def testModel(rootPackage: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + // just need to check the member exists, access methods will throw an error if there's a problem + assert(!rootPackage.templates.exists(_.name == "a"), "package a should not exist in the root package") + assert(rootPackage.templates.exists(_.name == "b"), "package b should exist in the root package") + val b = rootPackage._package("b") + val B = b._class("B") + val D = b._class("D") + testDiagram(B, B.contentDiagram, 2, 1) + // unfortunately not all packages, as B1 extends A.this.A1 and it gets the wrong member -- maybe we should model + // things as we do for symbols? + testDiagram(D, D.contentDiagram, 3, 2) + } +}
\ No newline at end of file diff --git a/test/scaladoc/run/SI-5784.check b/test/scaladoc/run/SI-5784.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/SI-5784.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/SI-5784.scala b/test/scaladoc/run/SI-5784.scala new file mode 100644 index 0000000000..318eb78b2a --- /dev/null +++ b/test/scaladoc/run/SI-5784.scala @@ -0,0 +1,44 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + override def resourceFile: String = "SI-5784.scala" + + // no need for special settings + def scaladocSettings = "-diagrams" + + def testModel(rootPackage: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + val main = rootPackage._package("test")._package("templates") + + val String = main._aliasTypeTpl("String") + assert(String.companion.isDefined, "test.templates.String should have a pseudo-companion object") + + val Base = main._trait("Base") + assert(Base.members.filter(_.inDefinitionTemplates.head == Base).length == 5, Base.members.filter(_.inDefinitionTemplates.head == Base).length + " == 5") + assert(Base.members.collect{case d: DocTemplateEntity => d}.length == 4, Base.members.collect{case d: DocTemplateEntity => d}.length == 4) + testDiagram(Base, Base.contentDiagram, 2, 1) + + val BaseT = Base._absTypeTpl("T") + val Foo = Base._trait("Foo") + assert(BaseT.members.filter(_.inDefinitionTemplates.head == Base).length == 0, BaseT.members.filter(_.inDefinitionTemplates.head == Base).length + " == 0") + assert(BaseT.members.map(_.name).sorted == Foo.members.map(_.name).sorted, BaseT.members.map(_.name).sorted + " == " + Foo.members.map(_.name).sorted) + assert(BaseT.companion.isDefined, "test.templates.Base.T should have a pseudo-companion object") + testDiagram(BaseT, BaseT.inheritanceDiagram, 2, 1) + + val Api = main._trait("Api") + assert(Api.members.filter(_.inDefinitionTemplates.head == Api).length == 2, Api.members.filter(_.inDefinitionTemplates.head == Api).length + " == 2") // FooApi and override type T + assert(Api.members.collect{case d: DocTemplateEntity => d}.length == 5, Api.members.collect{case d: DocTemplateEntity => d}.length == 5) + testDiagram(Api, Api.contentDiagram, 3, 2) + + val ApiT = Api._absTypeTpl("T") + val FooApi = Api._trait("FooApi") + assert(ApiT.members.filter(_.inDefinitionTemplates.head == Api).length == 0, ApiT.members.filter(_.inDefinitionTemplates.head == Api).length + " == 0") + assert(ApiT.members.map(_.name).sorted == FooApi.members.map(_.name).sorted, ApiT.members.map(_.name).sorted + " == " + FooApi.members.map(_.name).sorted) + assert(ApiT.companion.isDefined, "test.templates.Api.T should have a pseudo-companion object") + testDiagram(ApiT, ApiT.inheritanceDiagram, 2, 1) + } +}
\ No newline at end of file diff --git a/test/scaladoc/run/SI-5965.check b/test/scaladoc/run/SI-5965.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/SI-5965.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/SI-5965.scala b/test/scaladoc/run/SI-5965.scala new file mode 100644 index 0000000000..6f4540d239 --- /dev/null +++ b/test/scaladoc/run/SI-5965.scala @@ -0,0 +1,24 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + override def code = """ + abstract class Param + class Test + object Test { + def apply(i: Int): Test = new Test + def apply(i: Int, p: Param = new Param { }): Test = new Test + } + """ + + // no need for special settings + def scaladocSettings = "" + + def testModel(rootPackage: Package) = { + import access._ + + // just need to make sure the model exists + val base = rootPackage._object("Test") + } +}
\ No newline at end of file diff --git a/test/scaladoc/run/diagrams-base.scala b/test/scaladoc/run/diagrams-base.scala index 38bed06502..b7aeed51d2 100644 --- a/test/scaladoc/run/diagrams-base.scala +++ b/test/scaladoc/run/diagrams-base.scala @@ -42,7 +42,7 @@ object Test extends ScaladocModelTest { assert(diag.nodes.filter(_.isThisNode).length == 1) // 1. check class E diagram - assert(diag.isClassDiagram) + assert(diag.isInheritanceDiagram) val (incoming, outgoing) = diag.edges.partition(!_._1.isThisNode) assert(incoming.length == 5) @@ -56,14 +56,14 @@ object Test extends ScaladocModelTest { assert(incomingSubclass.length == 2) assert(incomingImplicit.length == 3) - val classDiag = diag.asInstanceOf[ClassDiagram] + val classDiag = diag.asInstanceOf[InheritanceDiagram] assert(classDiag.incomingImplicits.length == 3) assert(classDiag.outgoingImplicits.length == 1) // 2. check package diagram // NOTE: Z should be eliminated because it's isolated val packDiag = base.contentDiagram.get - assert(packDiag.isPackageDiagram) + assert(packDiag.isContentDiagram) assert(packDiag.nodes.length == 8) // check singular object removal assert(packDiag.edges.length == 4) assert(packDiag.edges.foldLeft(0)(_ + _._2.length) == 6) diff --git a/test/scaladoc/run/diagrams-determinism.scala b/test/scaladoc/run/diagrams-determinism.scala index 6c8db05d78..2b6f8eecc7 100644 --- a/test/scaladoc/run/diagrams-determinism.scala +++ b/test/scaladoc/run/diagrams-determinism.scala @@ -49,19 +49,19 @@ object Test extends ScaladocModelTest { assert(run2 == run3) // 2. check the order in the diagram: this node, subclasses, and then implicit conversions - def assertRightOrder(diagram: Diagram) = { + def assertRightOrder(template: DocTemplateEntity, diagram: Diagram) = for ((node, subclasses) <- diagram.edges) assert(subclasses == subclasses.filter(_.isThisNode) ::: - subclasses.filter(_.isNormalNode) ::: - subclasses.filter(_.isImplicitNode)) - } + subclasses.filter(node => node.isNormalNode || node.isOutsideNode) ::: + subclasses.filter(_.isImplicitNode), + "Diagram order for " + template + ": " + subclasses) val base = rootPackage._package("scala")._package("test")._package("scaladoc")._package("diagrams") - assertRightOrder(base.contentDiagram.get) - assertRightOrder(base._trait("A").inheritanceDiagram.get) - assertRightOrder(base._trait("B").inheritanceDiagram.get) - assertRightOrder(base._trait("C").inheritanceDiagram.get) - assertRightOrder(base._trait("D").inheritanceDiagram.get) - assertRightOrder(base._trait("E").inheritanceDiagram.get) + assertRightOrder(base, base.contentDiagram.get) + assertRightOrder(base._trait("A"), base._trait("A").inheritanceDiagram.get) + assertRightOrder(base._trait("B"), base._trait("B").inheritanceDiagram.get) + assertRightOrder(base._trait("C"), base._trait("C").inheritanceDiagram.get) + assertRightOrder(base._trait("D"), base._trait("D").inheritanceDiagram.get) + assertRightOrder(base._trait("E"), base._trait("E").inheritanceDiagram.get) } }
\ No newline at end of file diff --git a/test/scaladoc/run/diagrams-filtering.scala b/test/scaladoc/run/diagrams-filtering.scala index 8cb32180a1..54e3e9ac63 100644 --- a/test/scaladoc/run/diagrams-filtering.scala +++ b/test/scaladoc/run/diagrams-filtering.scala @@ -65,7 +65,7 @@ object Test extends ScaladocModelTest { assert(!C.inheritanceDiagram.isDefined) // trait G - val G = base._trait("G") + val G = base._class("G") assert(!G.inheritanceDiagram.isDefined) // trait E diff --git a/test/scaladoc/run/groups.check b/test/scaladoc/run/groups.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/groups.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/groups.scala b/test/scaladoc/run/groups.scala new file mode 100644 index 0000000000..05324c2ec9 --- /dev/null +++ b/test/scaladoc/run/groups.scala @@ -0,0 +1,119 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + override def code = """ + package test.scaladoc.groups { + + /** + * The trait A + * @groupdesc A Group A is the group that contains functions starting with f + * For example: + * {{{ + * this is an example + * }}} + * @groupdesc B Group B is the group that contains functions starting with b + * @groupname B Group B has a nice new name and a high priority + * @groupprio B -10 + * @group Traits + * @note This is a note + */ + trait A { + /** foo description + * @group A */ + def foo = 1 + + /** bar description + * @group B */ + def bar = 2 + } + + /** The trait B + * @group Traits + * @groupdesc C Group C is introduced by B + */ + trait B { + /** baz descriptopn + * @group C */ + def baz = 3 + } + + /** The class C which inherits from both A and B + * @group Classes + * @groupdesc B Look ma, I'm overriding group descriptions!!! + * @groupname B And names + */ + class C extends A with B { + /** Oh noes, I lost my group -- or did I?!? */ + override def baz = 4 + } + } + """ + + // no need for special settings + def scaladocSettings = "-feature" + + def testModel(rootPackage: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + // just need to check the member exists, access methods will throw an error if there's a problem + val base = rootPackage._package("test")._package("scaladoc")._package("groups") + + def checkGroup(mbr: MemberEntity, grp: String) = + assert(mbr.group == grp, "Incorrect group for " + mbr.qualifiedName + ": " + mbr.group + " instead of " + grp) + + def checkGroupDesc(dtpl: DocTemplateEntity, grp: String, grpDesc: String) = { + assert(dtpl.groupDescription(grp).isDefined, + "Group description for " + grp + " not defined in " + dtpl.qualifiedName) + assert(extractCommentText(dtpl.groupDescription(grp).get).contains(grpDesc), + "Group description for " + grp + " in " + dtpl.qualifiedName + " does not contain \"" + grpDesc + "\": \"" + + extractCommentText(dtpl.groupDescription(grp).get) + "\"") + } + + def checkGroupName(dtpl: DocTemplateEntity, grp: String, grpName: String) = + // TODO: See why we need trim here, we already do trimming in the CommentFactory + assert(dtpl.groupName(grp) == grpName, + "Group name for " + grp + " in " + dtpl.qualifiedName + " does not equal \"" + grpName + "\": \"" + dtpl.groupName(grp) + "\"") + + def checkGroupPrio(dtpl: DocTemplateEntity, grp: String, grpPrio: Int) = + assert(dtpl.groupPriority(grp) == grpPrio, + "Group priority for " + grp + " in " + dtpl.qualifiedName + " does not equal " + grpPrio + ": " + dtpl.groupPriority(grp)) + + + val A = base._trait("A") + val B = base._trait("B") + val C = base._class("C") + checkGroup(A, "Traits") + checkGroup(B, "Traits") + checkGroup(C, "Classes") + checkGroup(A._method("foo"), "A") + checkGroup(A._method("bar"), "B") + checkGroup(B._method("baz"), "C") + checkGroup(C._method("foo"), "A") + checkGroup(C._method("bar"), "B") + checkGroup(C._method("baz"), "C") + + checkGroupDesc(A, "A", "Group A is the group that contains functions starting with f") + checkGroupName(A, "A", "A") + checkGroupPrio(A, "A", 0) + checkGroupDesc(A, "B", "Group B is the group that contains functions starting with b") + checkGroupName(A, "B", "Group B has a nice new name and a high priority") + checkGroupPrio(A, "B", -10) + + checkGroupDesc(B, "C", "Group C is introduced by B") + checkGroupName(B, "C", "C") + checkGroupPrio(B, "C", 0) + + checkGroupDesc(C, "A", "Group A is the group that contains functions starting with f") + checkGroupName(C, "A", "A") + checkGroupPrio(C, "A", 0) + checkGroupDesc(C, "B", "Look ma, I'm overriding group descriptions!!!") + checkGroupName(C, "B", "And names") + checkGroupPrio(C, "B", -10) + checkGroupDesc(C, "C", "Group C is introduced by B") + checkGroupName(C, "C", "C") + checkGroupPrio(C, "C", 0) + } +}
\ No newline at end of file diff --git a/test/scaladoc/run/implicits-ambiguating.scala b/test/scaladoc/run/implicits-ambiguating.scala index 1420593b74..05daf1f805 100644 --- a/test/scaladoc/run/implicits-ambiguating.scala +++ b/test/scaladoc/run/implicits-ambiguating.scala @@ -17,7 +17,7 @@ object Test extends ScaladocModelTest { mbr.byConversion.map(_.source.implicitsShadowing.get(mbr).map(_.isAmbiguous).getOrElse(false)).getOrElse(false) // SEE THE test/resources/implicits-chaining-res.scala FOR THE EXPLANATION OF WHAT'S CHECKED HERE: - val base = root._package("scala")._package("test")._package("scaladoc")._package("implicits")._object("ambiguating") + val base = root._package("scala")._package("test")._package("scaladoc")._package("implicits")._package("ambiguating") var conv1: ImplicitConversion = null var conv2: ImplicitConversion = null diff --git a/test/scaladoc/run/implicits-known-type-classes.check b/test/scaladoc/run/implicits-known-type-classes.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/implicits-known-type-classes.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/implicits-known-type-classes.scala b/test/scaladoc/run/implicits-known-type-classes.scala new file mode 100644 index 0000000000..9f4ca372b0 --- /dev/null +++ b/test/scaladoc/run/implicits-known-type-classes.scala @@ -0,0 +1,33 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest +import language._ + +object Test extends ScaladocModelTest { + + // test a file instead of a piece of code + override def resourceFile = "implicits-known-type-classes-res.scala" + + // start implicits + def scaladocSettings = "-implicits" + + def testModel(root: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + /** Tests the "known type classes" feature of scaladoc implicits + * if the test fails, please update the correct qualified name of + * the type class in src/compiler/scala/tools/nsc/doc/Settings.scala + * in the knownTypeClasses map. Thank you! */ + + val base = root._package("scala")._package("test")._package("scaladoc")._package("implicits")._package("typeclasses") + var conv: ImplicitConversion = null + + val A = base._class("A") + + for (conversion <- A.conversions if !conversion.isCommonConversion) { + assert(conversion.constraints.length == 1, conversion.constraints.length + " == 1 (in " + conversion + ")") + assert(conversion.constraints.head.isInstanceOf[KnownTypeClassConstraint], + conversion.constraints.head + " is not a known type class constraint!") + } + } +} diff --git a/test/scaladoc/run/implicits-scopes.scala b/test/scaladoc/run/implicits-scopes.scala index 7b9e80e148..d91deba326 100644 --- a/test/scaladoc/run/implicits-scopes.scala +++ b/test/scaladoc/run/implicits-scopes.scala @@ -24,7 +24,7 @@ object Test extends ScaladocModelTest { val test1 = base._package("test1") val A = test1._class("A") - conv = A._conversion(test1.qualifiedName + ".package.toB") // the .package means it's the package object + conv = A._conversion(test1.qualifiedName + ".toB") assert(conv.members.length == 1) assert(conv.constraints.length == 0) } @@ -36,7 +36,9 @@ object Test extends ScaladocModelTest { val classes = test2._package("classes") val A = classes._class("A") - assert(A._conversions(test2.qualifiedName + ".toB").isEmpty) + conv = A._conversion(test2.qualifiedName + ".toB") + assert(conv.members.length == 1) + assert(conv.constraints.length == 0) } //// test3 ///////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/test/scaladoc/run/implicits-shadowing.scala b/test/scaladoc/run/implicits-shadowing.scala index 2827d31122..6869b12a94 100644 --- a/test/scaladoc/run/implicits-shadowing.scala +++ b/test/scaladoc/run/implicits-shadowing.scala @@ -17,7 +17,7 @@ object Test extends ScaladocModelTest { mbr.byConversion.map(_.source.implicitsShadowing.get(mbr).map(_.isShadowed).getOrElse(false)).getOrElse(false) // SEE THE test/resources/implicits-chaining-res.scala FOR THE EXPLANATION OF WHAT'S CHECKED HERE: - val base = root._package("scala")._package("test")._package("scaladoc")._package("implicits")._object("shadowing") + val base = root._package("scala")._package("test")._package("scaladoc")._package("implicits")._package("shadowing") var conv: ImplicitConversion = null //// class A /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/test/scaladoc/run/implicits-var-exp.scala b/test/scaladoc/run/implicits-var-exp.scala index 16569fe3c2..94d2990d29 100644 --- a/test/scaladoc/run/implicits-var-exp.scala +++ b/test/scaladoc/run/implicits-var-exp.scala @@ -6,25 +6,36 @@ object Test extends ScaladocModelTest { override def code = """ package scala.test.scaladoc.variable.expansion { - /** - * Blah blah blah - */ + /** @define coll WROOOONG-A */ class A object A { import language.implicitConversions - implicit def aToB(a: A) = new B + implicit def aToC(a: A) = new C + implicit def aToE(a: A) = new E with F } - /** - * @define coll collection - */ + /** @define coll WROOOONG-B */ class B { - /** - * foo returns a $coll - */ + /** foo returns a $coll */ def foo: Nothing = ??? } + + /** @define coll collection */ + class C extends B + + /** @define coll WROOOONG-D */ + trait D { + /** bar returns a $coll */ + def bar: Nothing = ??? + } + + /** @define coll result */ + //trait E { self: D => override def bar: Nothing = ??? } + trait E extends D { override def bar: Nothing = ??? } + + /** @define coll WROOOONG-F */ + trait F } """ @@ -37,7 +48,9 @@ object Test extends ScaladocModelTest { val base = rootPackage._package("scala")._package("test")._package("scaladoc")._package("variable")._package("expansion") val foo = base._class("A")._method("foo") - assert(foo.comment.get.body.toString.contains("foo returns a collection"), "\"" + foo.comment.get.body.toString + "\".contains(\"foo returns a collection\")") + + val bar = base._class("A")._method("bar") + assert(bar.comment.get.body.toString.contains("bar returns a result"), "\"" + bar.comment.get.body.toString + "\".contains(\"bar returns a result\")") } }
\ No newline at end of file diff --git a/test/scaladoc/run/links.check b/test/scaladoc/run/links.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/links.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/links.scala b/test/scaladoc/run/links.scala new file mode 100644 index 0000000000..40ce6368ce --- /dev/null +++ b/test/scaladoc/run/links.scala @@ -0,0 +1,28 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +// SI-5079 "Scaladoc can't link to an object (only a class or trait)" +// SI-4497 "Links in ScalaDoc - Spec and implementation unsufficient" +// SI-4224 "Wiki-links should support method targets" +// SI-3695 "support non-fully-qualified type links in scaladoc comments" +object Test extends ScaladocModelTest { + + override def resourceFile = "links.scala" + + // no need for special settings + def scaladocSettings = "" + + def testModel(rootPackage: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + // just need to check the member exists, access methods will throw an error if there's a problem + val base = rootPackage._package("scala")._package("test")._package("scaladoc")._package("links") + val TEST = base._object("TEST") + + val memberLinks = countLinks(TEST.comment.get, _.link.isInstanceOf[LinkToMember]) + val templateLinks = countLinks(TEST.comment.get, _.link.isInstanceOf[LinkToTpl]) + assert(memberLinks == 14, memberLinks + " == 14 (the member links in object TEST)") + assert(templateLinks == 2, templateLinks + " == 2 (the template links in object TEST)") + } +}
\ No newline at end of file diff --git a/test/scaladoc/scalacheck/CommentFactoryTest.scala b/test/scaladoc/scalacheck/CommentFactoryTest.scala index b576ba5544..5e3141bdc0 100644 --- a/test/scaladoc/scalacheck/CommentFactoryTest.scala +++ b/test/scaladoc/scalacheck/CommentFactoryTest.scala @@ -10,7 +10,13 @@ import scala.tools.nsc.doc.model.diagram._ class Factory(val g: Global, val s: doc.Settings) extends doc.model.ModelFactory(g, s) { - thisFactory: Factory with ModelFactoryImplicitSupport with DiagramFactory with CommentFactory with doc.model.TreeFactory => + thisFactory: Factory + with ModelFactoryImplicitSupport + with ModelFactoryTypeSupport + with DiagramFactory + with CommentFactory + with doc.model.TreeFactory + with MemberLookup => def strip(c: Comment): Option[Inline] = { c.body match { @@ -31,7 +37,13 @@ object Test extends Properties("CommentFactory") { val settings = new doc.Settings((str: String) => {}) val reporter = new scala.tools.nsc.reporters.ConsoleReporter(settings) val g = new Global(settings, reporter) - (new Factory(g, settings) with ModelFactoryImplicitSupport with DiagramFactory with CommentFactory with doc.model.TreeFactory) + (new Factory(g, settings) + with ModelFactoryImplicitSupport + with ModelFactoryTypeSupport + with DiagramFactory + with CommentFactory + with doc.model.TreeFactory + with MemberLookup) } def parse(src: String, dst: Inline) = { diff --git a/test/scaladoc/scalacheck/HtmlFactoryTest.scala b/test/scaladoc/scalacheck/HtmlFactoryTest.scala index 5b6f75426e..13eacf79a5 100644 --- a/test/scaladoc/scalacheck/HtmlFactoryTest.scala +++ b/test/scaladoc/scalacheck/HtmlFactoryTest.scala @@ -235,30 +235,6 @@ object Test extends Properties("HtmlFactory") { } } - property("Trac #3484") = { - val files = createTemplates("Trac3484.scala") - - files("Collection.html") match { - case node: scala.xml.Node => { - val s = node.toString - s.contains("""<span class="result">: Traversable[B]</span>""") - } - case _ => false - } - } - - property("Trac #3484 - SR704") = { - val files = createTemplates("Trac3484.scala") - - files("SR704.html") match { - case node: scala.xml.Node => { - val s = node.toString - s.contains("Hello Mister John.") - } - case _ => false - } - } - property("Trac #4325 - files") = { val files = createTemplates("Trac4325.scala") @@ -303,7 +279,7 @@ object Test extends Properties("HtmlFactory") { case _ => false } } - // + // // property("Trac #484 - refinements and existentials") = { // val files = createTemplates("Trac484.scala") // val lines = """ @@ -315,7 +291,7 @@ object Test extends Properties("HtmlFactory") { // |def j(x: Int): Bar // |def k(): AnyRef { type Dingus <: T forSome { type T <: String } } // """.stripMargin.trim.lines map (_.trim) - // + // // files("RefinementAndExistentials.html") match { // case node: scala.xml.Node => { // val s = node.text.replaceAll("\\s+", " ") @@ -397,26 +373,17 @@ object Test extends Properties("HtmlFactory") { } } - property("Should decode symbolic type alias name.") = { + property("SI-4714: Should decode symbolic type alias name.") = { createTemplate("SI_4715.scala") match { case node: scala.xml.Node => { val html = node.toString - html.contains(">: :+:[<") - } - case _ => false - } - } - - property("Shouldn't drop type arguments to aliased tuple.") = { - createTemplate("SI_4676.scala") match { - case node: scala.xml.Node => { - node.toString.contains(">ss: (String, String)<") + html.contains(">:+:<") } case _ => false } } - property("Default arguments of synthesized constructor") = { + property("SI-4287: Default arguments of synthesized constructor") = { val files = createTemplates("SI_4287.scala") files("ClassWithSugar.html") match { @@ -427,7 +394,7 @@ object Test extends Properties("HtmlFactory") { } } - property("Default arguments of synthesized constructor") = { + property("SI-4507: Default arguments of synthesized constructor") = { createTemplate("SI_4507.scala") match { case node: scala.xml.Node => ! node.toString.contains("<li>returns silently when evaluating true and true</li>") @@ -435,40 +402,40 @@ object Test extends Properties("HtmlFactory") { } } - property("Use cases and links should not crash scaladoc") = { + property("SI-4898: Use cases and links should not crash scaladoc") = { createTemplate("SI_4898.scala") true } - property("Use cases should override their original members") = + property("SI-5054: Use cases should override their original members") = checkText("SI_5054_q1.scala")( (None,"""def test(): Int""", true) //Disabled because the full signature is now displayed //(None,"""def test(implicit lost: Int): Int""", false) ) - property("Use cases should keep their flags - final should not be lost") = + property("SI-5054: Use cases should keep their flags - final should not be lost") = checkText("SI_5054_q2.scala")((None, """final def test(): Int""", true)) - property("Use cases should keep their flags - implicit should not be lost") = + property("SI-5054: Use cases should keep their flags - implicit should not be lost") = checkText("SI_5054_q3.scala")((None, """implicit def test(): Int""", true)) - property("Use cases should keep their flags - real abstract should not be lost") = + property("SI-5054: Use cases should keep their flags - real abstract should not be lost") = checkText("SI_5054_q4.scala")((None, """abstract def test(): Int""", true)) - property("Use cases should keep their flags - traits should not be affected") = + property("SI-5054: Use cases should keep their flags - traits should not be affected") = checkText("SI_5054_q5.scala")((None, """def test(): Int""", true)) - property("Use cases should keep their flags - traits should not be affected") = + property("SI-5054: Use cases should keep their flags - traits should not be affected") = checkText("SI_5054_q6.scala")((None, """abstract def test(): Int""", true)) - property("Use case individual signature test") = + property("SI-5054: Use case individual signature test") = checkText("SI_5054_q7.scala")( (None, """abstract def test2(explicit: Int): Int [use case] This takes the explicit value passed.""", true), (None, """abstract def test1(): Int [use case] This takes the implicit value in scope.""", true) ) - property("Display correct \"Definition classes\"") = + property("SI-5287: Display correct \"Definition classes\"") = checkText("SI_5287.scala")( (None, """def method(): Int @@ -477,7 +444,7 @@ object Test extends Properties("HtmlFactory") { Definition Classes SI_5287 SI_5287_B SI_5287_A""", true) ) // the explanation appears twice, as small comment and full comment - property("Correct comment inheritance for overriding") = + property("Comment inheritance: Correct comment inheritance for overriding") = checkText("implicit-inheritance-override.scala")( (Some("Base"), """def function[T](arg1: T, arg2: String): Double @@ -521,7 +488,7 @@ object Test extends Properties("HtmlFactory") { ) for (useCaseFile <- List("UseCaseInheritance", "UseCaseOverrideInheritance")) { - property("Correct comment inheritance for usecases") = + property("Comment inheritance: Correct comment inheritance for usecases") = checkText("implicit-inheritance-usecase.scala")( (Some(useCaseFile), """def missing_arg[T](arg1: T): Double @@ -588,7 +555,7 @@ object Test extends Properties("HtmlFactory") { ) } - property("Correct explicit inheritance for override") = + property("Comment inheritance: Correct explicit inheritance for override") = checkText("explicit-inheritance-override.scala")( (Some("InheritDocDerived"), """def function[T](arg1: T, arg2: String): Double @@ -614,7 +581,7 @@ object Test extends Properties("HtmlFactory") { See also StartSee The Manual EndSee """, true)) - property("Correct explicit inheritance for usecase") = + property("Comment inheritance: Correct explicit inheritance for usecase") = checkText("explicit-inheritance-usecase.scala")( (Some("UseCaseInheritDoc"), """def function[T](arg1: T, arg2: String): Double @@ -639,7 +606,7 @@ object Test extends Properties("HtmlFactory") { See also StartSee The Manual EndSee """, true)) - property("Correct explicit inheritance in corner cases") = + property("Comment inheritance: Correct explicit inheritance in corner cases") = checkText("inheritdoc-corner-cases.scala")( (Some("D"), """def hello1: Int diff --git a/test/scaladoc/scalacheck/IndexTest.scala b/test/scaladoc/scalacheck/IndexTest.scala index 29e337da2b..bf385898fc 100644 --- a/test/scaladoc/scalacheck/IndexTest.scala +++ b/test/scaladoc/scalacheck/IndexTest.scala @@ -16,7 +16,7 @@ object Test extends Properties("Index") { val morepaths = Thread.currentThread.getContextClassLoader.getParent.asInstanceOf[URLClassLoader].getURLs.map(u => URLDecoder.decode(u.getPath)) (paths ++ morepaths).mkString(java.io.File.pathSeparator) } - + val docFactory = { val settings = new doc.Settings({Console.err.println(_)}) @@ -27,9 +27,9 @@ object Test extends Properties("Index") { new doc.DocFactory(reporter, settings) } - + val indexModelFactory = doc.model.IndexModelFactory - + def createIndex(path: String): Option[Index] = { val maybeUniverse = { @@ -79,4 +79,11 @@ object Test extends Properties("Index") { case None => false } } + property("package objects in index") = { + createIndex("test/scaladoc/resources/SI-5558.scala") match { + case Some(index) => + index.index.firstLetterIndex('f') isDefinedAt "foo" + case None => false + } + } } |