diff options
279 files changed, 5308 insertions, 2025 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/Enclosures.scala b/src/compiler/scala/reflect/makro/runtime/Enclosures.scala index 80c35d22ff..360a4b8e8a 100644 --- a/src/compiler/scala/reflect/makro/runtime/Enclosures.scala +++ b/src/compiler/scala/reflect/makro/runtime/Enclosures.scala @@ -14,7 +14,6 @@ trait Enclosures { // vals are eager to simplify debugging // after all we wouldn't save that much time by making them lazy val macroApplication: Tree = expandee - val enclosingApplication: Tree = enclTrees collectFirst { case t: Apply => t } getOrElse EmptyTree val enclosingClass: Tree = site.enclClass.tree val enclosingImplicits: List[(Type, Tree)] = site.openImplicits val enclosingMacros: List[Context] = this :: universe.analyzer.openMacros // include self 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 0051c3bdec..1775602122 100644 --- a/src/compiler/scala/tools/nsc/Driver.scala +++ b/src/compiler/scala/tools/nsc/Driver.scala @@ -1,10 +1,12 @@ package scala.tools.nsc import scala.tools.nsc.reporters.{Reporter, ConsoleReporter} -import Properties.{ versionString, copyrightString } +import Properties.{ versionString, copyrightString, residentPromptString } import scala.reflect.internal.util.{ BatchSourceFile, FakePos } abstract class Driver { + + val prompt = residentPromptString val versionMsg = "Scala compiler " + versionString + " -- " + @@ -51,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 6a61beb55d..e07702fc41 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1401,6 +1401,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/Main.scala b/src/compiler/scala/tools/nsc/Main.scala index 19c872b6d3..8b7e76e994 100644 --- a/src/compiler/scala/tools/nsc/Main.scala +++ b/src/compiler/scala/tools/nsc/Main.scala @@ -12,15 +12,13 @@ import scala.tools.nsc.interactive.{ RefinedBuildManager, SimpleBuildManager } import scala.tools.nsc.io.AbstractFile import scala.tools.nsc.reporters.{Reporter, ConsoleReporter} import scala.reflect.internal.util.{ BatchSourceFile, FakePos } //{Position} -import Properties.{ versionString, copyrightString, residentPromptString, msilLibPath } +import Properties.msilLibPath /** The main class for NSC, a compiler for the programming - * language Scala. + * language Scala. */ object Main extends Driver with EvalLoop { - val prompt = residentPromptString - def resident(compiler: Global) { loop { line => val args = line.split(' ').toList diff --git a/src/compiler/scala/tools/nsc/MainBench.scala b/src/compiler/scala/tools/nsc/MainBench.scala new file mode 100644 index 0000000000..0037de7b94 --- /dev/null +++ b/src/compiler/scala/tools/nsc/MainBench.scala @@ -0,0 +1,48 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.tools.nsc + +import java.io.File +import File.pathSeparator + +import scala.tools.nsc.interactive.{ RefinedBuildManager, SimpleBuildManager } +import scala.tools.nsc.io.AbstractFile +import scala.tools.nsc.reporters.{Reporter, ConsoleReporter} +import scala.reflect.internal.util.{ BatchSourceFile, FakePos } //{Position} +import Properties.{ versionString, copyrightString, residentPromptString, msilLibPath } +import scala.reflect.internal.util.Statistics + +/** The main class for NSC, a compiler for the programming + * language Scala. + */ +object MainBench extends Driver with EvalLoop { + + lazy val theCompiler = Global(settings, reporter) + + override def newCompiler() = theCompiler + + val NIter = 50 + val NBest = 10 + + override def main(args: Array[String]) = { + val times = new Array[Long](NIter) + var start = System.nanoTime() + for (i <- 0 until NIter) { + if (i == NIter-1) { + theCompiler.settings.Ystatistics.value = true + Statistics.enabled = true + } + process(args) + val end = System.nanoTime() + val duration = (end-start)/1000000 + println(s"${duration}ms") + times(i) = duration + start = end + } + val avg = times.sorted.take(NBest).sum / NBest + println(s"avg shortest $NBest times ${avg}ms") + } +} 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 f1baeca8d0..1de510747d 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 eb9b4b7dd3..1b4b4334bb 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 280afb441f..62b5d69cff 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 76f02ed141..68f5e0e394 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 2e0cc1a7c3..d934e9e3a3 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 5a8bbfff15..05a77ee979 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/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index 0c988ceae4..9b4e793241 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -34,8 +34,7 @@ abstract class SymbolLoaders { /** Enter class with given `name` into scope of `root` * and give them `completer` as type. */ - def enterClass(root: Symbol, name: String, completer: SymbolLoader): Symbol = { - val owner = root.ownerOfNewSymbols + def enterClass(owner: Symbol, name: String, completer: SymbolLoader): Symbol = { val clazz = owner.newClass(newTypeName(name)) clazz setInfo completer enterIfNew(owner, clazz, completer) @@ -44,8 +43,7 @@ abstract class SymbolLoaders { /** Enter module with given `name` into scope of `root` * and give them `completer` as type. */ - def enterModule(root: Symbol, name: String, completer: SymbolLoader): Symbol = { - val owner = root.ownerOfNewSymbols + def enterModule(owner: Symbol, name: String, completer: SymbolLoader): Symbol = { val module = owner.newModule(newTermName(name)) module setInfo completer module.moduleClass setInfo moduleClassLoader @@ -217,15 +215,18 @@ abstract class SymbolLoaders { root.setInfo(new PackageClassInfoType(newScope, root)) val sourcepaths = classpath.sourcepaths - for (classRep <- classpath.classes if platform.doLoad(classRep)) { - initializeFromClassPath(root, classRep) + if (!root.isRoot) { + for (classRep <- classpath.classes if platform.doLoad(classRep)) { + initializeFromClassPath(root, classRep) + } } + if (!root.isEmptyPackageClass) { + for (pkg <- classpath.packages) { + enterPackage(root, pkg.name, new PackageLoader(pkg)) + } - for (pkg <- classpath.packages) { - enterPackage(root, pkg.name, new PackageLoader(pkg)) + openPackageModule(root) } - - openPackageModule(root) } } 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 b692482972..8ffbdcf066 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 4c9d855413..492273dfdc 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -373,7 +373,7 @@ abstract class TailCalls extends Transform { // the labels all look like: matchEnd(x) {x} // then, in a forward jump `matchEnd(expr)`, `expr` is considered in tail position (and the matchEnd jump is replaced by the jump generated by expr) class TailPosLabelsTraverser extends Traverser { - val tailLabels = new collection.mutable.ListBuffer[Symbol]() + val tailLabels = new collection.mutable.HashSet[Symbol]() private var maybeTail: Boolean = true // since we start in the rhs of a DefDef @@ -388,9 +388,18 @@ abstract class TailCalls extends Transform { def traverseTreesNoTail(trees: List[Tree]) = trees foreach traverseNoTail override def traverse(tree: Tree) = tree match { - case LabelDef(_, List(arg), body@Ident(_)) if arg.symbol == body.symbol => // we're looking for label(x){x} in tail position, since that means `a` is in tail position in a call `label(a)` + // we're looking for label(x){x} in tail position, since that means `a` is in tail position in a call `label(a)` + case LabelDef(_, List(arg), body@Ident(_)) if arg.symbol == body.symbol => if (maybeTail) tailLabels += tree.symbol + // jumps to matchEnd are transparent; need this case for nested matches + // (and the translated match case below does things in reverse for this case's sake) + 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) @@ -400,9 +409,9 @@ abstract class TailCalls extends Transform { // the assumption is once we encounter a case, the remainder of the block will consist of cases // the prologue may be empty, usually it is the valdef that stores the scrut val (prologue, cases) = stats span (s => !s.isInstanceOf[LabelDef]) - traverseTreesNoTail(prologue) // selector (may be absent) - traverseTrees(cases) traverse(expr) + traverseTrees(cases.reverse) // reverse so that we enter the matchEnd LabelDef before we see jumps to it + traverseTreesNoTail(prologue) // selector (may be absent) case CaseDef(pat, guard, body) => traverse(body) @@ -426,7 +435,7 @@ abstract class TailCalls extends Transform { traverseTreesNoTail(catches) traverseNoTail(finalizer) - case EmptyTree | Super(_, _) | This(_) | Select(_, _) | Ident(_) | Literal(_) | Function(_, _) | TypeTree() => + case Apply(_, _) | EmptyTree | Super(_, _) | This(_) | Select(_, _) | Ident(_) | Literal(_) | Function(_, _) | TypeTree() => case _ => super.traverse(tree) } } diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index a5bfac0e03..ebf0ecd634 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -33,6 +33,14 @@ import language.postfixOps * - convert implicit method types to method types * - convert non-trivial catches in try statements to matches * - convert non-local returns to throws with enclosing try statements. + * - convert try-catch expressions in contexts where there might be values on the stack to + * a local method and a call to it (since an exception empties the evaluation stack): + * + * meth(x_1,..., try { x_i } catch { ..}, .. x_b0) ==> + * { + * def liftedTry$1 = try { x_i } catch { .. } + * meth(x_1, .., liftedTry$1(), .. ) + * } */ /*</export> */ abstract class UnCurry extends InfoTransform @@ -217,38 +225,15 @@ abstract class UnCurry extends InfoTransform } - /* Transform a function node (x_1,...,x_n) => body of type FunctionN[T_1, .., T_N, R] to + /** Transform a function node (x_1,...,x_n) => body of type FunctionN[T_1, .., T_N, R] to * * class $anon() extends AbstractFunctionN[T_1, .., T_N, R] with Serializable { * def apply(x_1: T_1, ..., x_N: T_n): R = body * } * new $anon() * - * transform a function node (x => body) of type PartialFunction[T, R] where - * body = expr match { case P_i if G_i => E_i }_i=1..n - * to: + * If `settings.XoldPatmat.value`, also synthesized AbstractPartialFunction subclasses (see synthPartialFunction). * - //TODO: correct code template below - * class $anon() extends AbstractPartialFunction[T, R] with Serializable { - * def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = (expr: @unchecked) match { - * case P_1 if G_1 => E_1 - * ... - * case P_n if G_n => E_n - * case _ => default(expr) - * } - * def isDefinedAt(x: T): boolean = (x: @unchecked) match { - * case P_1 if G_1 => true - * ... - * case P_n if G_n => true - * case _ => false - * } - * } - * new $anon() - * - * However, if one of the patterns P_i if G_i is a default pattern, - * drop the last default clause in the definition of `apply` and generate for `_isDefinedAt` instead - * - * def isDefinedAtCurrent(x: T): boolean = true */ def transformFunction(fun: Function): Tree = deEta(fun) match { @@ -292,6 +277,28 @@ abstract class UnCurry extends InfoTransform } + /** Transform a function node (x => body) of type PartialFunction[T, R] where + * body = expr match { case P_i if G_i => E_i }_i=1..n + * to (assuming none of the cases is a default case): + * + * class $anon() extends AbstractPartialFunction[T, R] with Serializable { + * def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = (expr: @unchecked) match { + * case P_1 if G_1 => E_1 + * ... + * case P_n if G_n => E_n + * case _ => default(expr) + * } + * def isDefinedAt(x: T): boolean = (x: @unchecked) match { + * case P_1 if G_1 => true + * ... + * case P_n if G_n => true + * case _ => false + * } + * } + * new $anon() + * + * If there's a default case, the original match is used for applyOrElse, and isDefinedAt returns `true` + */ def synthPartialFunction(fun: Function) = { if (!settings.XoldPatmat.value) debugwarn("Under the new pattern matching scheme, PartialFunction should have been synthesized during typers.") @@ -634,6 +641,13 @@ abstract class UnCurry extends InfoTransform case ret @ Return(_) if (isNonLocalReturn(ret)) => withNeedLift(true) { super.transform(ret) } + case Try(_, Nil, _) => + // try-finally does not need lifting: lifting is needed only for try-catch + // expressions that are evaluated in a context where the stack might not be empty. + // `finally` does not attempt to continue evaluation after an exception, so the fact + // that values on the stack are 'lost' does not matter + super.transform(tree) + case Try(block, catches, finalizer) => if (needTryLift || shouldBeLiftedAnyway(tree)) transform(liftTree(tree)) else super.transform(tree) diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index bcf529ecd2..805f60ba87 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -636,6 +636,12 @@ trait Contexts { self: Analyzer => collect(imp.tree.selectors) } + /* SI-5892 / SI-4270: `implicitss` can return results which are not accessible at the + * point where implicit search is triggered. Example: implicits in (annotations of) + * class type parameters (SI-5892). The `context.owner` is the class symbol, therefore + * `implicitss` will return implicit conversions defined inside the class. These are + * filtered out later by `eligibleInfos` (SI-4270 / 9129cfe9), as they don't type-check. + */ def implicitss: List[List[ImplicitInfo]] = { if (implicitsRunId != currentRunId) { implicitsRunId = currentRunId 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/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 48fd6ba928..0a0bf9d710 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1214,8 +1214,8 @@ trait Namers extends MethodSynthesis { if (!annotated.isInitialized) tree match { case defn: MemberDef => val ainfos = defn.mods.annotations filterNot (_ eq null) map { ann => - // need to be lazy, #1782 - AnnotationInfo lazily (typer typedAnnotation ann) + // need to be lazy, #1782. enteringTyper to allow inferView in annotation args, SI-5892. + AnnotationInfo lazily enteringTyper(typer typedAnnotation ann) } if (ainfos.nonEmpty) { annotated setAnnotations ainfos diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index 5c94ff63a6..99e978fb51 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -15,6 +15,7 @@ import scala.tools.nsc.transform.Transform import scala.collection.mutable.HashSet import scala.collection.mutable.HashMap import reflect.internal.util.Statistics +import scala.reflect.internal.Types /** Translate pattern matching. * @@ -53,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 (!settings.XoldPatmat.value) new MatchTransformer(unit) else noopTransformer @@ -71,7 +91,15 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL case Match(sel, cases) => val origTp = tree.tpe // setType origTp intended for CPS -- TODO: is it necessary? - localTyper.typed(translator.translateMatch(treeCopy.Match(tree, transform(sel), transformTrees(cases).asInstanceOf[List[CaseDef]]))) setType origTp + val translated = translator.translateMatch(treeCopy.Match(tree, transform(sel), transformTrees(cases).asInstanceOf[List[CaseDef]])) + try { + localTyper.typed(translated) setType origTp + } catch { + case x: (Types#TypeError) => + // TODO: this should never happen; error should've been reported during type checking + unit.error(tree.pos, "error during expansion of this match (this is a scalac bug).\nThe underlying error was: "+ x.msg) + translated + } case Try(block, catches, finalizer) => treeCopy.Try(tree, transform(block), translator.translateTry(transformTrees(catches).asInstanceOf[List[CaseDef]], tree.tpe, tree.pos), transform(finalizer)) case _ => super.transform(tree) @@ -413,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) => @@ -430,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 +")") @@ -547,7 +575,10 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL if(isSeq) { val TypeRef(pre, SeqClass, args) = seqTp // do repeated-parameter expansion to match up with the expected number of arguments (in casu, subpatterns) - formalTypes(rawSubPatTypes.init :+ typeRef(pre, RepeatedParamClass, args), nbSubPats) + val formalsWithRepeated = rawSubPatTypes.init :+ typeRef(pre, RepeatedParamClass, args) + + if (lastIsStar) formalTypes(formalsWithRepeated, nbSubPats - 1) :+ seqTp + else formalTypes(formalsWithRepeated, nbSubPats) } else rawSubPatTypes protected def rawSubPatTypes: List[Type] @@ -637,7 +668,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // binder has type paramType def treeMaker(binder: Symbol, pos: Position): TreeMaker = { // checks binder ne null before chaining to the next extractor - ProductExtractorTreeMaker(binder, lengthGuard(binder))(Substitution(subPatBinders, subPatRefs(binder))) + ProductExtractorTreeMaker(binder, lengthGuard(binder))(subPatBinders, subPatRefs(binder)) } // reference the (i-1)th case accessor if it exists, otherwise the (i-1)th tuple component @@ -681,7 +712,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // the extractor call (applied to the binder bound by the flatMap corresponding to the previous (i.e., enclosing/outer) pattern) val extractorApply = atPos(pos)(spliceApply(patBinderOrCasted)) val binder = freshSym(pos, pureType(resultInMonad)) // can't simplify this when subPatBinders.isEmpty, since UnitClass.tpe is definitely wrong when isSeq, and resultInMonad should always be correct since it comes directly from the extractor's result type - ExtractorTreeMaker(extractorApply, lengthGuard(binder), binder)(Substitution(subPatBinders, subPatRefs(binder)), resultType.typeSymbol == BooleanClass, checkedLength, patBinderOrCasted) + ExtractorTreeMaker(extractorApply, lengthGuard(binder), binder)(subPatBinders, subPatRefs(binder), resultType.typeSymbol == BooleanClass, checkedLength, patBinderOrCasted) } override protected def seqTree(binder: Symbol): Tree = @@ -837,6 +868,20 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } private[this] var currSub: Substitution = null + /** The substitution that specifies the trees that compute the values of the subpattern binders. + * + * Should not be used to perform actual substitution! + * Only used to reason symbolically about the values the subpattern binders are bound to. + * See TreeMakerToCond#updateSubstitution. + * + * Overridden in PreserveSubPatBinders to pretend it replaces the subpattern binders by subpattern refs + * (Even though we don't do so anymore -- see SI-5158, SI-5739 and SI-6070.) + * + * TODO: clean this up, would be nicer to have some higher-level way to compute + * the binders bound by this tree maker and the symbolic values that correspond to them + */ + def subPatternsAsSubstitution: Substitution = substitution + // build Tree that chains `next` after the current extractor def chainBefore(next: Tree)(casegen: Casegen): Tree } @@ -885,32 +930,89 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL atPos(pos)(casegen.flatMapCond(cond, res, nextBinder, substitution(next))) } + trait PreserveSubPatBinders extends NoNewBinders { + val subPatBinders: List[Symbol] + val subPatRefs: List[Tree] + + /** The substitution that specifies the trees that compute the values of the subpattern binders. + * + * We pretend to replace the subpattern binders by subpattern refs + * (Even though we don't do so anymore -- see SI-5158, SI-5739 and SI-6070.) + */ + override def subPatternsAsSubstitution = + Substitution(subPatBinders, subPatRefs) >> super.subPatternsAsSubstitution + + import CODE._ + def bindSubPats(in: Tree): Tree = Block(map2(subPatBinders, subPatRefs)(VAL(_) === _), in) + } + /** * Make a TreeMaker that will result in an extractor call specified by `extractor` * the next TreeMaker (here, we don't know which it'll be) is chained after this one by flatMap'ing * a function with binder `nextBinder` over our extractor's result * the function's body is determined by the next TreeMaker - * in this function's body, and all the subsequent ones, references to the symbols in `from` will be replaced by the corresponding tree in `to` + * (furthermore, the interpretation of `flatMap` depends on the codegen instance we're using). + * + * The values for the subpatterns, as computed by the extractor call in `extractor`, + * are stored in local variables that re-use the symbols in `subPatBinders`. + * This makes extractor patterns more debuggable (SI-5739). */ - case class ExtractorTreeMaker(extractor: Tree, extraCond: Option[Tree], nextBinder: Symbol)(val localSubstitution: Substitution, extractorReturnsBoolean: Boolean, val checkedLength: Option[Int], val prevBinder: Symbol) extends FunTreeMaker { + case class ExtractorTreeMaker(extractor: Tree, extraCond: Option[Tree], nextBinder: Symbol)( + val subPatBinders: List[Symbol], + val subPatRefs: List[Tree], + extractorReturnsBoolean: Boolean, + val checkedLength: Option[Int], + val prevBinder: Symbol) extends FunTreeMaker with PreserveSubPatBinders { + def chainBefore(next: Tree)(casegen: Casegen): Tree = { - val condAndNext = extraCond map (casegen.ifThenElseZero(_, next)) getOrElse next + val condAndNext = extraCond match { + case Some(cond) => + casegen.ifThenElseZero(substitution(cond), bindSubPats(substitution(next))) + case _ => + bindSubPats(substitution(next)) + } atPos(extractor.pos)( - if (extractorReturnsBoolean) casegen.flatMapCond(extractor, CODE.UNIT, nextBinder, substitution(condAndNext)) - else casegen.flatMap(extractor, nextBinder, substitution(condAndNext)) + if (extractorReturnsBoolean) casegen.flatMapCond(extractor, CODE.UNIT, nextBinder, condAndNext) + else casegen.flatMap(extractor, nextBinder, condAndNext) ) } override def toString = "X"+(extractor, nextBinder.name) } - // TODO: allow user-defined unapplyProduct - case class ProductExtractorTreeMaker(prevBinder: Symbol, extraCond: Option[Tree])(val localSubstitution: Substitution) extends FunTreeMaker { import CODE._ + /** + * An optimized version of ExtractorTreeMaker for Products. + * For now, this is hard-coded to case classes, and we simply extract the case class fields. + * + * The values for the subpatterns, as specified by the case class fields at the time of extraction, + * are stored in local variables that re-use the symbols in `subPatBinders`. + * This makes extractor patterns more debuggable (SI-5739) as well as + * avoiding mutation after the pattern has been matched (SI-5158, SI-6070) + * + * TODO: make this user-definable as follows + * When a companion object defines a method `def unapply_1(x: T): U_1`, but no `def unapply` or `def unapplySeq`, + * the extractor is considered to match any non-null value of type T + * the pattern is expected to have as many sub-patterns as there are `def unapply_I(x: T): U_I` methods, + * and the type of the I'th sub-pattern is `U_I`. + * The same exception for Seq patterns applies: if the last extractor is of type `Seq[U_N]`, + * the pattern must have at least N arguments (exactly N if the last argument is annotated with `: _*`). + * The arguments starting at N (and beyond) are taken from the sequence returned by apply_N, + * and it is checked that that sequence has enough elements to provide values for all expected sub-patterns. + * + * For a case class C, the implementation is assumed to be `def unapply_I(x: C) = x._I`, + * and the extractor call is inlined under that assumption. + */ + case class ProductExtractorTreeMaker(prevBinder: Symbol, extraCond: Option[Tree])( + val subPatBinders: List[Symbol], + val subPatRefs: List[Tree]) extends FunTreeMaker with PreserveSubPatBinders { + + import CODE._ val nextBinder = prevBinder // just passing through + def chainBefore(next: Tree)(casegen: Casegen): Tree = { val nullCheck = REF(prevBinder) OBJ_NE NULL val cond = extraCond map (nullCheck AND _) getOrElse nullCheck - casegen.ifThenElseZero(cond, substitution(next)) + casegen.ifThenElseZero(cond, bindSubPats(substitution(next))) } override def toString = "P"+(prevBinder.name, extraCond getOrElse "", localSubstitution) @@ -1024,7 +1126,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL else expectedTp match { // TODO: [SPEC] the spec requires `eq` instead of `==` for singleton types // this implies sym.isStable - case SingleType(_, sym) => and(equalsTest(CODE.REF(sym), testedBinder), typeTest(testedBinder, expectedTp.widen)) + case SingleType(_, sym) => and(equalsTest(gen.mkAttributedQualifier(expectedTp), testedBinder), typeTest(testedBinder, expectedTp.widen)) // must use == to support e.g. List() == Nil case ThisType(sym) if sym.isModule => and(equalsTest(CODE.REF(sym), testedBinder), typeTest(testedBinder, expectedTp.widen)) case ConstantType(Constant(null)) if testedBinder.info.widen <:< AnyRefClass.tpe @@ -1541,7 +1643,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL * TODO: don't ignore outer-checks */ def apply(tm: TreeMaker): Cond = { - if (!substitutionComputed) updateSubstitution(tm.substitution) + if (!substitutionComputed) updateSubstitution(tm.subPatternsAsSubstitution) tm match { case ttm@TypeTestTreeMaker(prevBinder, testedBinder, pt, _) => @@ -1863,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 @@ -1896,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 { @@ -1918,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 @@ -1935,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 @@ -1954,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 @@ -2357,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) @@ -2425,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 } } @@ -2568,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/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 063ebe5614..c448547ea1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -119,7 +119,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R // those with the DEFAULTPARAM flag, and infer the methods. Looking for the methods // directly requires inspecting the parameter list of every one. That modification // shaved 95% off the time spent in this method. - val defaultGetters = clazz.info.findMember(nme.ANYNAME, 0L, DEFAULTPARAM, false).alternatives + val defaultGetters = clazz.info.findMembers(0L, DEFAULTPARAM) val defaultMethodNames = defaultGetters map (sym => nme.defaultGetterToMethod(sym.name)) defaultMethodNames.distinct foreach { name => 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 53f9429572..568a3a9c14 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 _ => } @@ -3883,40 +3884,51 @@ trait Typers extends Modes with Adaptations with Tags { } } - def typedBind(name: Name, body: Tree) = { - var vble = tree.symbol - def typedBindType(name: TypeName) = { - assert(body == EmptyTree, context.unit + " typedBind: " + name.debugString + " " + body + " " + body.getClass) - if (vble == NoSymbol) - vble = - if (isFullyDefined(pt)) - context.owner.newAliasType(name, tree.pos) setInfo pt - else - context.owner.newAbstractType(name, tree.pos) setInfo TypeBounds.empty - val rawInfo = vble.rawInfo - vble = if (vble.name == tpnme.WILDCARD) context.scope.enter(vble) - else namer.enterInScope(vble) - tree setSymbol vble setType vble.tpe - } - def typedBindTerm(name: TermName) = { - if (vble == NoSymbol) - vble = context.owner.newValue(name, tree.pos) - if (vble.name.toTermName != nme.WILDCARD) { - if ((mode & ALTmode) != 0) - VariableInPatternAlternativeError(tree) - vble = namer.enterInScope(vble) - } - val body1 = typed(body, mode, pt) - vble.setInfo( - if (treeInfo.isSequenceValued(body)) seqType(body1.tpe) - else body1.tpe) - treeCopy.Bind(tree, name, body1) setSymbol vble setType body1.tpe // burak, was: pt - } + def typedBind(name: Name, body: Tree) = name match { - case x: TypeName => typedBindType(x) - case x: TermName => typedBindTerm(x) + case name: TypeName => assert(body == EmptyTree, context.unit + " typedBind: " + name.debugString + " " + body + " " + body.getClass) + val sym = + if (tree.symbol != NoSymbol) tree.symbol + else { + if (isFullyDefined(pt)) + context.owner.newAliasType(name, tree.pos) setInfo pt + else + context.owner.newAbstractType(name, tree.pos) setInfo TypeBounds.empty + } + + if (name != tpnme.WILDCARD) namer.enterInScope(sym) + else context.scope.enter(sym) + + tree setSymbol sym setType sym.tpe + + case name: TermName => + val sym = + if (tree.symbol != NoSymbol) tree.symbol + else context.owner.newValue(name, tree.pos) + + if (name != nme.WILDCARD) { + if ((mode & ALTmode) != 0) VariableInPatternAlternativeError(tree) + namer.enterInScope(sym) + } + + val body1 = typed(body, mode, pt) + val symTp = + if (treeInfo.isSequenceValued(body)) seqType(body1.tpe) + else body1.tpe + sym setInfo symTp + + // have to imperatively set the symbol for this bind to keep it in sync with the symbols used in the body of a case + // when type checking a case we imperatively update the symbols in the body of the case + // those symbols are bound by the symbols in the Binds in the pattern of the case, + // so, if we set the symbols in the case body, but not in the patterns, + // then re-type check the casedef (for a second try in typedApply for example -- SI-1832), + // we are no longer in sync: the body has symbols set that do not appear in the patterns + // since body1 is not necessarily equal to body, we must return a copied tree, + // but we must still mutate the original bind + tree setSymbol sym + treeCopy.Bind(tree, name, body1) setSymbol sym setType body1.tpe } - } + def typedArrayValue(elemtpt: Tree, elems: List[Tree]) = { val elemtpt1 = typedType(elemtpt, mode) @@ -4509,6 +4521,8 @@ trait Typers extends Modes with Adaptations with Tags { assert(errorContainer == null, "Cannot set ambiguous error twice for identifier") errorContainer = tree } + + val fingerPrint: Long = name.fingerPrint var defSym: Symbol = tree.symbol // the directly found symbol var pre: Type = NoPrefix // the prefix type of defSym, if a class member @@ -4547,7 +4561,10 @@ trait Typers extends Modes with Adaptations with Tags { var cx = startingIdentContext while (defSym == NoSymbol && cx != NoContext && (cx.scope ne null)) { // cx.scope eq null arises during FixInvalidSyms in Duplicators pre = cx.enclClass.prefix - defEntry = cx.scope.lookupEntry(name) + defEntry = { + val scope = cx.scope + if ((fingerPrint & scope.fingerPrints) != 0) scope.lookupEntry(name) else null + } if ((defEntry ne null) && qualifies(defEntry.sym)) { // Right here is where SI-1987, overloading in package objects, can be // seen to go wrong. There is an overloaded symbol, but when referring 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/Double.scala b/src/library/scala/Double.scala index 2ff46c433d..510de92a2a 100644 --- a/src/library/scala/Double.scala +++ b/src/library/scala/Double.scala @@ -370,9 +370,6 @@ object Double extends AnyValCompanion { final val PositiveInfinity = java.lang.Double.POSITIVE_INFINITY final val NegativeInfinity = java.lang.Double.NEGATIVE_INFINITY - @deprecated("use Double.MinPositiveValue instead", "2.9.0") - final val Epsilon = MinPositiveValue - /** The negative number with the greatest (finite) absolute value which is representable * by a Double. Note that it differs from [[java.lang.Double.MIN_VALUE]], which * is the smallest positive value representable by a Double. In Scala that number diff --git a/src/library/scala/Float.scala b/src/library/scala/Float.scala index bd7a07fece..b9c116da0b 100644 --- a/src/library/scala/Float.scala +++ b/src/library/scala/Float.scala @@ -370,9 +370,6 @@ object Float extends AnyValCompanion { final val PositiveInfinity = java.lang.Float.POSITIVE_INFINITY final val NegativeInfinity = java.lang.Float.NEGATIVE_INFINITY - @deprecated("use Float.MinPositiveValue instead", "2.9.0") - final val Epsilon = MinPositiveValue - /** The negative number with the greatest (finite) absolute value which is representable * by a Float. Note that it differs from [[java.lang.Float.MIN_VALUE]], which * is the smallest positive value representable by a Float. In Scala that number 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/immutable/Vector.scala b/src/library/scala/collection/immutable/Vector.scala index d100bf93df..4dfe147a65 100644 --- a/src/library/scala/collection/immutable/Vector.scala +++ b/src/library/scala/collection/immutable/Vector.scala @@ -18,8 +18,14 @@ import scala.collection.parallel.immutable.ParVector /** Companion object to the Vector class */ object Vector extends SeqFactory[Vector] { + private[collection] class VectorReusableCBF extends GenericCanBuildFrom[Nothing] { + override def apply() = newBuilder[Nothing] + } + + private val VectorReusableCBF: GenericCanBuildFrom[Nothing] = new VectorReusableCBF + @inline implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, Vector[A]] = - ReusableCBF.asInstanceOf[CanBuildFrom[Coll, A, Vector[A]]] + VectorReusableCBF.asInstanceOf[CanBuildFrom[Coll, A, Vector[A]]] def newBuilder[A]: Builder[A, Vector[A]] = new VectorBuilder[A] private[immutable] val NIL = new Vector[Nothing](0, 0, 0) @inline override def empty[A]: Vector[A] = NIL @@ -140,19 +146,19 @@ override def companion: GenericCompanion[Vector] = Vector // SeqLike api - @inline override def updated[B >: A, That](index: Int, elem: B)(implicit bf: CanBuildFrom[Vector[A], B, That]): That = { - // just ignore bf - updateAt(index, elem).asInstanceOf[That] + @inline override def updated[B >: A, That](index: Int, elem: B)(implicit bf: CanBuildFrom[Vector[A], B, That]): That = bf match { + case _: Vector.VectorReusableCBF => updateAt(index, elem).asInstanceOf[That] // just ignore bf + case _ => super.updated(index, elem)(bf) } - @inline override def +:[B >: A, That](elem: B)(implicit bf: CanBuildFrom[Vector[A], B, That]): That = { - // just ignore bf - appendFront(elem).asInstanceOf[That] + @inline override def +:[B >: A, That](elem: B)(implicit bf: CanBuildFrom[Vector[A], B, That]): That = bf match { + case _: Vector.VectorReusableCBF => appendFront(elem).asInstanceOf[That] // just ignore bf + case _ => super.+:(elem)(bf) } - @inline override def :+[B >: A, That](elem: B)(implicit bf: CanBuildFrom[Vector[A], B, That]): That = { - // just ignore bf - appendBack(elem).asInstanceOf[That] + @inline override def :+[B >: A, That](elem: B)(implicit bf: CanBuildFrom[Vector[A], B, That]): That = bf match { + case _: Vector.VectorReusableCBF => appendBack(elem).asInstanceOf[That] // just ignore bf + case _ => super.:+(elem)(bf) } override def take(n: Int): Vector[A] = { 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/BlockContext.scala b/src/library/scala/concurrent/BlockContext.scala index a5b878c546..640560a174 100644 --- a/src/library/scala/concurrent/BlockContext.scala +++ b/src/library/scala/concurrent/BlockContext.scala @@ -12,9 +12,10 @@ import java.lang.Thread import scala.concurrent.util.Duration /** - * A context to be notified by `scala.concurrent.blocking()` when + * A context to be notified by `scala.concurrent.blocking` when * a thread is about to block. In effect this trait provides - * the implementation for `scala.concurrent.blocking()`. `scala.concurrent.blocking()` + * the implementation for `scala.concurrent.Await`. + * `scala.concurrent.Await.result()` and `scala.concurrent.Await.ready()` * locates an instance of `BlockContext` by first looking for one * provided through `BlockContext.withBlockContext()` and failing that, * checking whether `Thread.currentThread` is an instance of `BlockContext`. @@ -27,11 +28,11 @@ import scala.concurrent.util.Duration * {{{ * val oldContext = BlockContext.current * val myContext = new BlockContext { - * override def internalBlockingCall[T](awaitable: Awaitable[T], atMost: Duration): T = { + * override def blockOn[T](thunk: =>T)(implicit permission: CanAwait): T = { * // you'd have code here doing whatever you need to do * // when the thread is about to block. * // Then you'd chain to the previous context: - * oldContext.internalBlockingCall(awaitable, atMost) + * oldContext.blockOn(thunk) * } * } * BlockContext.withBlockContext(myContext) { @@ -42,35 +43,33 @@ import scala.concurrent.util.Duration */ trait BlockContext { - /** Used internally by the framework; blocks execution for at most - * `atMost` time while waiting for an `awaitable` object to become ready. + /** Used internally by the framework; + * Designates (and eventually executes) a thunk which potentially blocks the calling `Thread`. * - * Clients should use `scala.concurrent.blocking` instead; this is - * the implementation of `scala.concurrent.blocking`, generally - * provided by a `scala.concurrent.ExecutionContext` or `java.util.concurrent.Executor`. + * Clients must use `scala.concurrent.blocking` or `scala.concurrent.Await` instead. */ - def internalBlockingCall[T](awaitable: Awaitable[T], atMost: Duration): T + def blockOn[T](thunk: =>T)(implicit permission: CanAwait): T } object BlockContext { private object DefaultBlockContext extends BlockContext { - override def internalBlockingCall[T](awaitable: Awaitable[T], atMost: Duration): T = - awaitable.result(atMost)(Await.canAwaitEvidence) + override def blockOn[T](thunk: =>T)(implicit permission: CanAwait): T = thunk } - private val contextLocal = new ThreadLocal[BlockContext]() { - override def initialValue = Thread.currentThread match { + private val contextLocal = new ThreadLocal[BlockContext]() + + /** Obtain the current thread's current `BlockContext`. */ + def current: BlockContext = contextLocal.get match { + case null => Thread.currentThread match { case ctx: BlockContext => ctx case _ => DefaultBlockContext } + case some => some } - /** Obtain the current thread's current `BlockContext`. */ - def current: BlockContext = contextLocal.get - /** Pushes a current `BlockContext` while executing `body`. */ def withBlockContext[T](blockContext: BlockContext)(body: => T): T = { - val old = contextLocal.get + val old = contextLocal.get // can be null try { contextLocal.set(blockContext) body diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index f82b79cb18..d24fdbf005 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -707,11 +707,9 @@ object Future { // doesn't need to create defaultExecutionContext as // a side effect. private[concurrent] object InternalCallbackExecutor extends ExecutionContext { - def execute(runnable: Runnable): Unit = + override def execute(runnable: Runnable): Unit = runnable.run() - def internalBlockingCall[T](awaitable: Awaitable[T], atMost: Duration): T = - throw new IllegalStateException("bug in scala.concurrent, called blocking() from internal callback") - def reportFailure(t: Throwable): Unit = + override def reportFailure(t: Throwable): Unit = throw new IllegalStateException("problem in scala.concurrent internal callback", t) } } diff --git a/src/library/scala/concurrent/default/SchedulerImpl.scala.disabled b/src/library/scala/concurrent/default/SchedulerImpl.scala.disabled deleted file mode 100644 index 241efa8857..0000000000 --- a/src/library/scala/concurrent/default/SchedulerImpl.scala.disabled +++ /dev/null @@ -1,44 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package scala.concurrent -package default - -import scala.concurrent.util.Duration - -private[concurrent] final class SchedulerImpl extends Scheduler { - private val timer = - new java.util.Timer(true) // the associated thread runs as a daemon - - def schedule(delay: Duration, frequency: Duration)(thunk: => Unit): Cancellable = ??? - - def scheduleOnce(delay: Duration, task: Runnable): Cancellable = { - val timerTask = new java.util.TimerTask { - def run(): Unit = - task.run() - } - timer.schedule(timerTask, delay.toMillis) - new Cancellable { - def cancel(): Unit = - timerTask.cancel() - } - } - - def scheduleOnce(delay: Duration)(task: => Unit): Cancellable = { - val timerTask = new java.util.TimerTask { - def run(): Unit = - task - } - timer.schedule(timerTask, delay.toMillis) - new Cancellable { - def cancel(): Unit = - timerTask.cancel() - } - } - -} diff --git a/src/library/scala/concurrent/default/TaskImpl.scala.disabled b/src/library/scala/concurrent/default/TaskImpl.scala.disabled deleted file mode 100644 index 8b4eb12d4f..0000000000 --- a/src/library/scala/concurrent/default/TaskImpl.scala.disabled +++ /dev/null @@ -1,313 +0,0 @@ -package scala.concurrent -package default - - - -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater -import scala.concurrent.forkjoin.{ ForkJoinPool, RecursiveAction, ForkJoinWorkerThread } -import scala.util.Try -import scala.util -import scala.concurrent.util.Duration -import scala.annotation.tailrec -import scala.util.control.NonFatal - - -private[concurrent] trait Completable[T] { -self: Future[T] => - - val executor: ExecutionContextImpl - - def newPromise[S]: Promise[S] = executor promise - - type Callback = Try[T] => Any - - def getState: State[T] - - def casState(oldv: State[T], newv: State[T]): Boolean - - protected def dispatch[U](r: Runnable) = executionContext execute r - - protected def processCallbacks(cbs: List[Callback], r: Try[T]) = - for (cb <- cbs) dispatch(new Runnable { - override def run() = cb(r) - }) - - def future: Future[T] = self - - def onComplete[U](callback: Try[T] => U): this.type = { - @tailrec def tryAddCallback(): Try[T] = { - getState match { - case p @ Pending(lst) => - val pt = p.asInstanceOf[Pending[T]] - if (casState(pt, Pending(callback :: pt.callbacks))) null - else tryAddCallback() - case Success(res) => util.Success(res) - case Failure(t) => util.Failure(t) - } - } - - val res = tryAddCallback() - if (res != null) dispatch(new Runnable { - override def run() = - try callback(res) - catch handledFutureException andThen { - t => Console.err.println(t) - } - }) - - this - } - - def isTimedout: Boolean = getState match { - case Failure(ft: FutureTimeoutException) => true - case _ => false - } - -} - -private[concurrent] class PromiseImpl[T](context: ExecutionContextImpl) -extends Promise[T] with Future[T] with Completable[T] { - - val executor: scala.concurrent.default.ExecutionContextImpl = context - - @volatile private var state: State[T] = _ - - val updater = AtomicReferenceFieldUpdater.newUpdater(classOf[PromiseImpl[T]], classOf[State[T]], "state") - - updater.set(this, Pending(List())) - - def casState(oldv: State[T], newv: State[T]): Boolean = { - updater.compareAndSet(this, oldv, newv) - } - - def getState: State[T] = { - updater.get(this) - } - - @tailrec private def tryCompleteState(completed: State[T]): List[Callback] = (getState: @unchecked) match { - case p @ Pending(cbs) => if (!casState(p, completed)) tryCompleteState(completed) else cbs - case _ => null - } - - def tryComplete(r: Try[T]) = r match { - case util.Failure(t) => tryFailure(t) - case util.Success(v) => trySuccess(v) - } - - override def trySuccess(value: T): Boolean = { - val cbs = tryCompleteState(Success(value)) - if (cbs == null) - false - else { - processCallbacks(cbs, util.Success(value)) - this.synchronized { - this.notifyAll() - } - true - } - } - - override def tryFailure(t: Throwable): Boolean = { - val wrapped = wrap(t) - val cbs = tryCompleteState(Failure(wrapped)) - if (cbs == null) - false - else { - processCallbacks(cbs, util.Failure(wrapped)) - this.synchronized { - this.notifyAll() - } - true - } - } - - def await(atMost: Duration)(implicit canawait: scala.concurrent.CanAwait): T = getState match { - case Success(res) => res - case Failure(t) => throw t - case _ => - this.synchronized { - while (true) - getState match { - case Pending(_) => this.wait() - case Success(res) => return res - case Failure(t) => throw t - } - } - sys.error("unreachable") - } - -} - -private[concurrent] class TaskImpl[T](context: ExecutionContextImpl, body: => T) -extends RecursiveAction with Task[T] with Future[T] with Completable[T] { - - val executor: ExecutionContextImpl = context - - @volatile private var state: State[T] = _ - - val updater = AtomicReferenceFieldUpdater.newUpdater(classOf[TaskImpl[T]], classOf[State[T]], "state") - - updater.set(this, Pending(List())) - - def casState(oldv: State[T], newv: State[T]): Boolean = { - updater.compareAndSet(this, oldv, newv) - } - - def getState: State[T] = { - updater.get(this) - } - - @tailrec private def tryCompleteState(completed: State[T]): List[Callback] = (getState: @unchecked) match { - case p @ Pending(cbs) => if (!casState(p, completed)) tryCompleteState(completed) else cbs - } - - def compute(): Unit = { - var cbs: List[Callback] = null - try { - val res = body - processCallbacks(tryCompleteState(Success(res)), util.Success(res)) - } catch { - case t if NonFatal(t) => - processCallbacks(tryCompleteState(Failure(t)), util.Failure(t)) - case t => - val ee = new ExecutionException(t) - processCallbacks(tryCompleteState(Failure(ee)), util.Failure(ee)) - throw t - } - } - - def start(): Unit = { - Thread.currentThread match { - case fj: ForkJoinWorkerThread if fj.getPool eq executor.pool => fork() - case _ => executor.pool.execute(this) - } - } - - // TODO FIXME: handle timeouts - def await(atMost: Duration): this.type = - await - - def await: this.type = { - this.join() - this - } - - def tryCancel(): Unit = - tryUnfork() - - def await(atMost: Duration)(implicit canawait: CanAwait): T = { - join() // TODO handle timeout also - (updater.get(this): @unchecked) match { - case Success(r) => r - case Failure(t) => throw t - } - } - -} - - -private[concurrent] sealed abstract class State[T] - - -case class Pending[T](callbacks: List[Try[T] => Any]) extends State[T] - - -case class Success[T](result: T) extends State[T] - - -case class Failure[T](throwable: Throwable) extends State[T] - - -private[concurrent] final class ExecutionContextImpl extends ExecutionContext { - import ExecutionContextImpl._ - - val pool = { - val p = new ForkJoinPool - p.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler { - def uncaughtException(t: Thread, throwable: Throwable) { - Console.err.println(throwable.getMessage) - throwable.printStackTrace(Console.err) - } - }) - p - } - - @inline - private def executeTask(task: RecursiveAction) { - if (Thread.currentThread.isInstanceOf[ForkJoinWorkerThread]) - task.fork() - else - pool execute task - } - - def execute(task: Runnable) { - val action = new RecursiveAction { def compute() { task.run() } } - executeTask(action) - } - - def execute[U](body: () => U) { - val action = new RecursiveAction { def compute() { body() } } - executeTask(action) - } - - def task[T](body: => T): Task[T] = { - new TaskImpl(this, body) - } - - def future[T](body: => T): Future[T] = { - val t = task(body) - t.start() - t.future - } - - def promise[T]: Promise[T] = - new PromiseImpl[T](this) - - def blocking[T](atMost: Duration)(body: =>T): T = blocking(body2awaitable(body), atMost) - - def blocking[T](awaitable: Awaitable[T], atMost: Duration): T = { - currentExecutionContext.get match { - case null => awaitable.await(atMost)(null) // outside - TODO - fix timeout case - case x if x eq this => this.blockingCall(awaitable) // inside an execution context thread on this executor - case x => x.blocking(awaitable, atMost) - } - } - - private def blockingCall[T](b: Awaitable[T]): T = b match { - case fj: TaskImpl[_] if fj.executor.pool eq pool => - fj.await(Duration.fromNanos(0)) - case _ => - var res: T = null.asInstanceOf[T] - @volatile var blockingDone = false - // TODO add exception handling here! - val mb = new ForkJoinPool.ManagedBlocker { - def block() = { - res = b.await(Duration.fromNanos(0))(CanAwaitEvidence) - blockingDone = true - true - } - def isReleasable = blockingDone - } - ForkJoinPool.managedBlock(mb, true) - res - } - - def reportFailure(t: Throwable): Unit = {} - -} - - -object ExecutionContextImpl { - - private[concurrent] def currentExecutionContext: ThreadLocal[ExecutionContext] = new ThreadLocal[ExecutionContext] { - override protected def initialValue = null - } - -} - - - - - - - diff --git a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala index 98f821652f..875a558887 100644 --- a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala +++ b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala @@ -13,7 +13,7 @@ package scala.concurrent.impl import java.util.concurrent.{ LinkedBlockingQueue, Callable, Executor, ExecutorService, Executors, ThreadFactory, TimeUnit, ThreadPoolExecutor } import java.util.Collection import scala.concurrent.forkjoin._ -import scala.concurrent.{ BlockContext, ExecutionContext, Awaitable, ExecutionContextExecutor, ExecutionContextExecutorService } +import scala.concurrent.{ BlockContext, ExecutionContext, Awaitable, CanAwait, ExecutionContextExecutor, ExecutionContextExecutorService } import scala.concurrent.util.Duration import scala.util.control.NonFatal @@ -37,15 +37,15 @@ private[scala] class ExecutionContextImpl private[impl] (es: Executor, reporter: def newThread(runnable: Runnable): Thread = wire(new Thread(runnable)) def newThread(fjp: ForkJoinPool): ForkJoinWorkerThread = wire(new ForkJoinWorkerThread(fjp) with BlockContext { - override def internalBlockingCall[T](awaitable: Awaitable[T], atMost: Duration): T = { + override def blockOn[T](thunk: =>T)(implicit permission: CanAwait): T = { var result: T = null.asInstanceOf[T] ForkJoinPool.managedBlock(new ForkJoinPool.ManagedBlocker { @volatile var isdone = false - def block(): Boolean = { - result = try awaitable.result(atMost)(scala.concurrent.Await.canAwaitEvidence) finally { isdone = true } + override def block(): Boolean = { + result = try thunk finally { isdone = true } true } - def isReleasable = isdone + override def isReleasable = isdone }) result } diff --git a/src/library/scala/concurrent/impl/Future.scala b/src/library/scala/concurrent/impl/Future.scala index 132e1d79e7..098008e958 100644 --- a/src/library/scala/concurrent/impl/Future.scala +++ b/src/library/scala/concurrent/impl/Future.scala @@ -10,31 +10,13 @@ package scala.concurrent.impl -import scala.concurrent.util.Duration -import scala.concurrent.{Awaitable, ExecutionContext, CanAwait} -import scala.collection.mutable.Stack +import scala.concurrent.ExecutionContext import scala.util.control.NonFatal -private[concurrent] trait Future[+T] extends scala.concurrent.Future[T] with Awaitable[T] { - -} private[concurrent] object Future { - - /** Wraps a block of code into an awaitable object. */ - private[concurrent] def body2awaitable[T](body: =>T) = new Awaitable[T] { - def ready(atMost: Duration)(implicit permit: CanAwait) = { - body - this - } - def result(atMost: Duration)(implicit permit: CanAwait) = body - } - - def boxedType(c: Class[_]): Class[_] = if (c.isPrimitive) scala.concurrent.Future.toBoxed(c) else c - - private[impl] class PromiseCompletingRunnable[T](body: => T) - extends Runnable { + class PromiseCompletingRunnable[T](body: => T) extends Runnable { val promise = new Promise.DefaultPromise[T]() override def run() = { @@ -44,7 +26,7 @@ private[concurrent] object Future { } } - def apply[T](body: =>T)(implicit executor: ExecutionContext): Future[T] = { + def apply[T](body: =>T)(implicit executor: ExecutionContext): scala.concurrent.Future[T] = { val runnable = new PromiseCompletingRunnable(body) executor.execute(runnable) runnable.promise.future diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala index 84638586cf..c2df9ac296 100644 --- a/src/library/scala/concurrent/impl/Promise.scala +++ b/src/library/scala/concurrent/impl/Promise.scala @@ -18,7 +18,7 @@ import scala.util.control.NonFatal -private[concurrent] trait Promise[T] extends scala.concurrent.Promise[T] with Future[T] { +private[concurrent] trait Promise[T] extends scala.concurrent.Promise[T] with scala.concurrent.Future[T] { def future: this.type = this } diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index 76703bf081..a2ef42fac8 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -9,6 +9,7 @@ package scala import scala.concurrent.util.Duration +import scala.annotation.implicitNotFound /** This package object contains primitives for concurrent and parallel programming. */ @@ -36,46 +37,59 @@ package object concurrent { */ def promise[T]()(implicit execctx: ExecutionContext): Promise[T] = Promise[T]() - /** Used to block on a piece of code which potentially blocks. + /** Used to designate a piece of code which potentially blocks, allowing the BlockContext to adjust the runtime's behavior. + * Properly marking blocking code may improve performance or avoid deadlocks. * - * @param body A piece of code which contains potentially blocking or long running calls. - * - * Calling this method may throw the following exceptions: - * - CancellationException - if the computation was cancelled - * - InterruptedException - in the case that a wait within the blockable object was interrupted - * - TimeoutException - in the case that the blockable object timed out - */ - def blocking[T](body: =>T): T = blocking(impl.Future.body2awaitable(body), Duration.Inf) - - /** Blocks on an awaitable object. + * If you have an `Awaitable` then you should use Await.result instead of `blocking`. * - * @param awaitable An object with a `block` method which runs potentially blocking or long running calls. + * @param body A piece of code which contains potentially blocking or long running calls. * * Calling this method may throw the following exceptions: * - CancellationException - if the computation was cancelled * - InterruptedException - in the case that a wait within the blockable object was interrupted * - TimeoutException - in the case that the blockable object timed out */ - def blocking[T](awaitable: Awaitable[T], atMost: Duration): T = - BlockContext.current.internalBlockingCall(awaitable, atMost) + @throws(classOf[Exception]) + def blocking[T](body: =>T): T = BlockContext.current.blockOn(body)(scala.concurrent.AwaitPermission) } -/* concurrency constructs */ 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 { - private[concurrent] implicit val canAwaitEvidence = new CanAwait {} - - def ready[T](awaitable: Awaitable[T], atMost: Duration): awaitable.type = { - blocking(awaitable, atMost) - awaitable - } - - def result[T](awaitable: Awaitable[T], atMost: Duration): T = { - blocking(awaitable, atMost) - } + /** + * 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/library/scala/reflect/package.scala b/src/library/scala/reflect/package.scala index 9f9d4089c4..d97f2ec633 100644 --- a/src/library/scala/reflect/package.scala +++ b/src/library/scala/reflect/package.scala @@ -65,3 +65,6 @@ package object reflect { @deprecated("Use `@scala.beans.ScalaBeanInfo` instead", "2.10.0") type ScalaBeanInfo = scala.beans.ScalaBeanInfo } + +/** An exception that indicates an error during Scala reflection */ +case class ScalaReflectionException(msg: String) extends Exception(msg) 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/partest/scala/tools/partest/nest/DirectRunner.scala b/src/partest/scala/tools/partest/nest/DirectRunner.scala index 6c239721c3..c254472342 100644 --- a/src/partest/scala/tools/partest/nest/DirectRunner.scala +++ b/src/partest/scala/tools/partest/nest/DirectRunner.scala @@ -59,8 +59,12 @@ trait DirectRunner { val futures = kindFiles map (f => (f, pool submit callable(manager runTest f))) toMap pool.shutdown() - if (!pool.awaitTermination(4, TimeUnit.HOURS)) + try if (!pool.awaitTermination(4, TimeUnit.HOURS)) NestUI.warning("Thread pool timeout elapsed before all tests were complete!") + catch { case t: InterruptedException => + NestUI.warning("Thread pool was interrupted") + t.printStackTrace() + } for ((file, future) <- futures) yield { val state = if (future.isCancelled) TestState.Timeout else future.get diff --git a/src/reflect/scala/reflect/api/Mirrors.scala b/src/reflect/scala/reflect/api/Mirrors.scala index 348ab3656b..27176a2a2d 100644 --- a/src/reflect/scala/reflect/api/Mirrors.scala +++ b/src/reflect/scala/reflect/api/Mirrors.scala @@ -27,28 +27,63 @@ trait Mirrors { self: Universe => /** The instance value reflected by this mirror */ def instance: Any - /** The symbol corresponding to the run-time class of the reflected instance. */ + /** The symbol corresponding to the run-time class of the reflected instance */ def symbol: ClassSymbol - /** Get value of field in reflected instance. - * @field A field symbol that should represent a field of the instance class. - * @return The value associated with that field in the reflected instance - * @throws ??? + /** Reflects against a field symbol and returns a mirror + * that can be used to get and, if appropriate, set the value of the field. + * + * To get a field symbol by the name of the field you would like to reflect, + * use `<this mirror>.symbol.typeSignature.member(newTermName(<name of the field>)).asTermSymbol`. + * For further information about member lookup refer to `Symbol.typeSignature`. + * + * The input symbol can be either private or non-private (Scala reflection transparently deals with visibility). + * It must be a member (declared or inherited) of the class of the instance underlying this mirror. + * + * The input symbol can represent either a field itself or one of the corresponding accessors + * (in all cases the resulting mirror will refer to the field symbol). + * + * If a field symbol doesn't correspond to a reflectable entity of the underlying platform, + * a `ScalaReflectionException` exception will be thrown. This might happen, for example, for primary constructor parameters. + * Typically they produce class fields, however, private parameters that aren't used outside the constructor + * remain plain parameters of a constructor method of the class. */ def reflectField(field: TermSymbol): FieldMirror - /** Invokes a method on the reflected instance. - * @param meth A method symbol that should represent a method of the instance class - * @param args The arguments to pass to the method - * @return The result of invoking `meth(args)` on the reflected instance. - * @throws ??? + /** Reflects against a method symbol and returns a mirror + * that can be used to invoke the method provided. + * + * To get a method symbol by the name of the method you would like to reflect, + * use `<this mirror>.symbol.typeSignature.member(newTermName(<name of the method>)).asMethodSymbol`. + * For further information about member lookup refer to `Symbol.typeSignature`. + * + * The input symbol can be either private or non-private (Scala reflection transparently deals with visibility). + * It must be a member (declared or inherited) of the instance underlying this mirror. */ def reflectMethod(method: MethodSymbol): MethodMirror - /** .. */ + /** Reflects against an inner class symbol and returns a mirror + * that can be used to create instances of the class, inspect its companion object or perform further reflections. + * + * To get a class symbol by the name of the class you would like to reflect, + * use `<this mirror>.symbol.typeSignature.member(newTypeName(<name of the class>)).asClassSymbol`. + * For further information about member lookup refer to `Symbol.typeSignature`. + * + * The input symbol can be either private or non-private (Scala reflection transparently deals with visibility). + * It must be a member (declared or inherited) of the instance underlying this mirror. + */ def reflectClass(cls: ClassSymbol): ClassMirror - /** .. */ + /** Reflects against an inner module symbol and returns a mirror + * that can be used to get the instance of the object or inspect its companion class. + * + * To get a module symbol by the name of the object you would like to reflect, + * use `<this mirror>.symbol.typeSignature.member(newTermName(<name of the object>)).asModuleSymbol`. + * For further information about member lookup refer to `Symbol.typeSignature`. + * + * The input symbol can be either private or non-private (Scala reflection transparently deals with visibility). + * It must be a member (declared or inherited) of the instance underlying this mirror. + */ def reflectModule(mod: ModuleSymbol): ModuleMirror } @@ -58,13 +93,31 @@ trait Mirrors { self: Universe => /** The object containing the field */ def receiver: AnyRef - /** The field symbol representing the field */ + /** The field symbol representing the field. + * + * In Scala `val` and `var` declarations are usually compiled down to a pair of + * a backing field and corresponding accessor/accessors, which means that a single + * declaration might correspond to up to three different symbols. Nevertheless + * the `FieldMirror.symbol` field always points to a backing field symbol. + */ def symbol: TermSymbol - /** Retrieves the value stored in the field */ + /** Retrieves the value stored in the field. + * + * Scala reflection uses reflection capabilities of the underlying platform, + * so `FieldMirror.get` might throw platform-specific exceptions associated + * with getting a field or invoking a getter method of the field. + */ def get: Any - /** Updates the value stored in the field */ + /** Updates the value stored in the field. + * + * If a field is immutable, a `ScalaReflectionException` will be thrown. + * + * Scala reflection uses reflection capabilities of the underlying platform, + * so `FieldMirror.get` might throw platform-specific exceptions associated + * with setting a field or invoking a setter method of the field. + */ def set(value: Any): Unit } @@ -77,8 +130,12 @@ trait Mirrors { self: Universe => /** The method symbol representing the method */ def symbol: MethodSymbol - /** The result of applying the method to the given arguments */ - // [Eugene+++] If it's a constructor, it should account for inner classes + /** The result of applying the method to the given arguments + * + * Scala reflection uses reflection capabilities of the underlying platform, + * so `FieldMirror.get` might throw platform-specific exceptions associated + * with invoking the corresponding method or constructor. + */ def apply(args: Any*): Any } @@ -97,7 +154,7 @@ trait Mirrors { self: Universe => */ def isStatic: Boolean - /** The Scala symbol corresponding to the reflected runtime class or module. */ + /** The Scala symbol corresponding to the reflected runtime class or object */ def symbol: Symbol /** Optionally, the mirror of the companion reflected by this mirror. @@ -116,7 +173,7 @@ trait Mirrors { self: Universe => /** A mirror that reflects a Scala object definition or the static parts of a runtime class */ trait ModuleMirror extends TemplateMirror { - /** The Scala module symbol corresponding to the reflected module. */ + /** The Scala module symbol corresponding to the reflected object */ override def symbol: ModuleSymbol /** If the reflected runtime class corresponds to a Scala object definition, @@ -137,15 +194,18 @@ trait Mirrors { self: Universe => /** A mirror that reflects the instance parts of a runtime class */ trait ClassMirror extends TemplateMirror { - /** The Scala class symbol corresponding to the reflected class. */ + /** The Scala class symbol corresponding to the reflected class */ override def symbol: ClassSymbol - /** Returns a fresh instance of by invoking that constructor. - * @throws InstantiationException if the class does not have a public - * constructor with an empty parameter list. - * @throws IllegalAccessException if the class or its constructor is not accessible. - * @throws ExceptionInInitializerError if the initialization of the constructor fails. - * @throws SecurityException if creating a new instance is not permitted. + /** Reflects against a constructor symbol and returns a mirror + * that can be used to invoke it and construct instances of this mirror's symbols. + * + * To get a constructor symbol you would like to reflect, + * use `<this mirror>.symbol.typeSignature.member(nme.CONSTRUCTOR).asMethodSymbol`. + * For further information about member lookup refer to `Symbol.typeSignature`. + * + * The input symbol can be either private or non-private (Scala reflection transparently deals with visibility). + * It must be a member (declared or inherited) of the class underlying this mirror. */ def reflectConstructor(constructor: MethodSymbol): MethodMirror @@ -161,24 +221,40 @@ trait Mirrors { self: Universe => /** A mirror that reflects instances and static classes */ trait ReflectiveMirror extends MirrorOf[Mirrors.this.type] { - /** A reflective mirror for the given object - * @param obj An arbitrary value - * @return The mirror for `obj`. + /** A reflective mirror for the given object. + * + * Such a mirror can be used to further reflect against the members of the object + * to get/set fields, invoke methods and inspect inner classes and objects. */ def reflect(obj: Any): InstanceMirror - /** .. */ + /** Reflects against a static class symbol and returns a mirror + * that can be used to create instances of the class, inspect its companion object or perform further reflections. + * + * To get a class symbol by the name of the class you would like to reflect, + * use `<this mirror>.classSymbol(<runtime class loaded by its name>)`. + * + * The input symbol can be either private or non-private (Scala reflection transparently deals with visibility). + * It must be static, i.e. either top-level or nested within one or several static objects. + */ def reflectClass(cls: ClassSymbol): ClassMirror - /** .. */ + /** Reflects against a static module symbol and returns a mirror + * that can be used to get the instance of the object or inspect its companion class. + * + * To get a module symbol by the name of its companion class you would like to reflect, + * use `<this mirror>.classSymbol(<runtime class loaded by its name>).companion.get`. + * + * The input symbol can be either private or non-private (Scala reflection transparently deals with visibility). + * It must be static, i.e. either top-level or nested within one or several static objects. + */ def reflectModule(mod: ModuleSymbol): ModuleMirror } /** The API of a mirror for a reflective universe */ trait RuntimeMirror extends ReflectiveMirror { self => - /** Maps a Scala type to the corresponding Java class object - */ + /** Maps a Scala type to the corresponding Java class object */ def runtimeClass(tpe: Type): RuntimeClass /** Maps a Scala class symbol to the corresponding Java class object @@ -198,7 +274,7 @@ trait Mirrors { self: Universe => def classSymbol(rtcls: RuntimeClass): ClassSymbol /** A module symbol for the specified runtime class. - * @return The module symbol for the runtime class in the current class loader. + * @return The module symbol for the runtime class in the current class loader. * @throws java.lang.ClassNotFoundException if no class with that name exists * @throws scala.reflect.internal.MissingRequirementError if no corresponding symbol exists * to do: throws anything else? diff --git a/src/reflect/scala/reflect/api/StandardNames.scala b/src/reflect/scala/reflect/api/StandardNames.scala index 9ec66b8531..eb1ecda900 100644 --- a/src/reflect/scala/reflect/api/StandardNames.scala +++ b/src/reflect/scala/reflect/api/StandardNames.scala @@ -43,7 +43,6 @@ trait StandardNames extends base.StandardNames { val SUPER_PREFIX_STRING: String val TRAIT_SETTER_SEPARATOR_STRING: String - val ANYNAME: TermName val FAKE_LOCAL_THIS: TermName val INITIALIZER: TermName val LAZY_LOCAL: TermName diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala index c283ae408e..2bcc95b9a8 100644 --- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -160,12 +160,7 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => */ final class LazyAnnotationInfo(lazyInfo: => AnnotationInfo) extends AnnotationInfo { private var forced = false - private lazy val forcedInfo = - try { - val result = lazyInfo - if (result.pos == NoPosition) result setPos pos - result - } finally forced = true + private lazy val forcedInfo = try lazyInfo finally forced = true def atp: Type = forcedInfo.atp def args: List[Tree] = forcedInfo.args 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 210af661ee..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 = @@ -180,7 +227,7 @@ trait Mirrors extends api.Mirrors { // Still fiddling with whether it's cleaner to do some of this setup here // or from constructors. The latter approach tends to invite init order issues. - EmptyPackageClass setInfo ClassInfoType(Nil, newPackageScope(EmptyPackageClass), EmptyPackageClass) + EmptyPackageClass setInfo rootLoader EmptyPackage setInfo EmptyPackageClass.tpe connectModuleToClass(EmptyPackage, EmptyPackageClass) @@ -231,7 +278,6 @@ trait Mirrors extends api.Mirrors { override def isEffectiveRoot = true override def isStatic = true override def isNestedClass = false - override def ownerOfNewSymbols = EmptyPackageClass } // The empty package, which holds all top level types without given packages. final object EmptyPackage extends ModuleSymbol(RootClass, NoPosition, nme.EMPTY_PACKAGE_NAME) with WellKnownSymbol { diff --git a/src/reflect/scala/reflect/internal/Names.scala b/src/reflect/scala/reflect/internal/Names.scala index 20da38fd63..72e6707f57 100644 --- a/src/reflect/scala/reflect/internal/Names.scala +++ b/src/reflect/scala/reflect/internal/Names.scala @@ -415,6 +415,9 @@ trait Names extends api.Names with LowPriorityNames { } else toString } + + @inline + final def fingerPrint: Long = (1L << start) /** TODO - find some efficiency. */ def append(ch: Char) = newName("" + this + ch) diff --git a/src/reflect/scala/reflect/internal/Scopes.scala b/src/reflect/scala/reflect/internal/Scopes.scala index ceacd2afb0..89e3c52de6 100644 --- a/src/reflect/scala/reflect/internal/Scopes.scala +++ b/src/reflect/scala/reflect/internal/Scopes.scala @@ -41,10 +41,15 @@ trait Scopes extends api.Scopes { self: SymbolTable => * This is necessary because when run from reflection every scope needs to have a * SynchronizedScope as mixin. */ - class Scope protected[Scopes] (initElems: ScopeEntry = null) extends Iterable[Symbol] { + class Scope protected[Scopes] (initElems: ScopeEntry = null, initFingerPrints: Long = 0L) extends Iterable[Symbol] { + + /** A bitset containing the last 6 bits of the start value of every name + * stored in this scope. + */ + var fingerPrints: Long = initFingerPrints protected[Scopes] def this(base: Scope) = { - this(base.elems) + this(base.elems, base.fingerPrints) nestinglevel = base.nestinglevel + 1 } @@ -95,7 +100,7 @@ trait Scopes extends api.Scopes { self: SymbolTable => * * @param e ... */ - protected def enter(e: ScopeEntry) { + protected def enterEntry(e: ScopeEntry) { elemsCache = null if (hashtable ne null) enterInHash(e) @@ -113,7 +118,11 @@ trait Scopes extends api.Scopes { self: SymbolTable => * * @param sym ... */ - def enter[T <: Symbol](sym: T): T = { enter(newScopeEntry(sym, this)); sym } + def enter[T <: Symbol](sym: T): T = { + fingerPrints |= sym.name.fingerPrint + enterEntry(newScopeEntry(sym, this)) + sym + } /** enter a symbol, asserting that no symbol with same name exists in scope * @@ -147,6 +156,7 @@ trait Scopes extends api.Scopes { self: SymbolTable => } def rehash(sym: Symbol, newname: Name) { + fingerPrints |= newname.fingerPrint if (hashtable ne null) { val index = sym.name.start & HASHMASK var e1 = hashtable(index) @@ -344,7 +354,7 @@ trait Scopes extends api.Scopes { self: SymbolTable => /** The empty scope (immutable). */ object EmptyScope extends Scope { - override def enter(e: ScopeEntry) { + override def enterEntry(e: ScopeEntry) { abort("EmptyScope.enter") } } diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 165e04863c..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" @@ -297,7 +298,7 @@ trait StdNames { val WHILE_PREFIX = "while$" // Compiler internal names - val ANYNAME: NameType = "<anyname>" + val ANYname: NameType = "<anyname>" val CONSTRUCTOR: NameType = "<init>" val EQEQ_LOCAL_VAR: NameType = "eqEqTemp$" val FAKE_LOCAL_THIS: NameType = "this$" @@ -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 759c77f717..636f1e2f01 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -813,11 +813,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ def isEffectiveRoot = false - /** For RootClass, this is EmptyPackageClass. For all other symbols, - * the symbol itself. - */ - def ownerOfNewSymbols = this - final def isLazyAccessor = isLazy && lazyAccessor != NoSymbol final def isOverridableMember = !(isClass || isEffectivelyFinal) && (this ne NoSymbol) && owner.isClass @@ -938,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/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 2c705b0b5b..7f991176a7 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -14,6 +14,7 @@ import Flags._ import scala.util.control.ControlThrowable import scala.annotation.tailrec import util.Statistics +import scala.runtime.ObjectRef /* A standard type pattern match: case ErrorType => @@ -668,7 +669,8 @@ trait Types extends api.Types { self: SymbolTable => * Note: unfortunately it doesn't work to exclude DEFERRED this way. */ def membersBasedOnFlags(excludedFlags: Long, requiredFlags: Long): List[Symbol] = - findMember(nme.ANYNAME, excludedFlags, requiredFlags, false).alternatives + findMembers(excludedFlags, requiredFlags) +// findMember(nme.ANYNAME, excludedFlags, requiredFlags, false).alternatives def memberBasedOnName(name: Name, excludedFlags: Long): Symbol = findMember(name, excludedFlags, 0, false) @@ -1018,6 +1020,72 @@ trait Types extends api.Types { self: SymbolTable => else (baseClasses.head.newOverloaded(this, alts)) } + def findMembers(excludedFlags: Long, requiredFlags: Long): List[Symbol] = { + // if this type contains type variables, put them to sleep for a while -- don't just wipe them out by + // replacing them by the corresponding type parameter, as that messes up (e.g.) type variables in type refinements + // without this, the matchesType call would lead to type variables on both sides + // of a subtyping/equality judgement, which can lead to recursive types being constructed. + // See (t0851) for a situation where this happens. + val suspension: List[TypeVar] = if (this.isGround) null else suspendTypeVarsInType(this) + + Statistics.incCounter(findMembersCount) + val start = Statistics.pushTimer(typeOpsStack, findMembersNanos) + + //Console.println("find member " + name.decode + " in " + this + ":" + this.baseClasses)//DEBUG + var members: Scope = null + var required = requiredFlags + var excluded = excludedFlags | DEFERRED + var continue = true + var self: Type = null + while (continue) { + continue = false + val bcs0 = baseClasses + var bcs = bcs0 + while (!bcs.isEmpty) { + val decls = bcs.head.info.decls + var entry = decls.elems + while (entry ne null) { + val sym = entry.sym + val flags = sym.flags + if ((flags & required) == required) { + val excl = flags & excluded + if (excl == 0L && + (// omit PRIVATE LOCALS unless selector class is contained in class owning the def. + (bcs eq bcs0) || + (flags & PrivateLocal) != PrivateLocal || + (bcs0.head.hasTransOwner(bcs.head)))) { + if (members eq null) members = newScope + var others: ScopeEntry = members.lookupEntry(sym.name) + var symtpe: Type = null + while ((others ne null) && { + val other = others.sym + (other ne sym) && + ((other.owner eq sym.owner) || + (flags & PRIVATE) != 0 || { + if (self eq null) self = this.narrow + if (symtpe eq null) symtpe = self.memberType(sym) + !(self.memberType(other) matches symtpe) + })}) { + others = members lookupNextEntry others + } + if (others eq null) members enter sym + } else if (excl == DEFERRED) { + continue = true + } + } + entry = entry.next + } // while (entry ne null) + // excluded = excluded | LOCAL + bcs = bcs.tail + } // while (!bcs.isEmpty) + required |= DEFERRED + excluded &= ~(DEFERRED.toLong) + } // while (continue) + Statistics.popTimer(typeOpsStack, start) + if (suspension ne null) suspension foreach (_.suspended = false) + if (members eq null) Nil else members.toList + } + /** * Find member(s) in this type. If several members matching criteria are found, they are * returned in an OverloadedSymbol @@ -1040,75 +1108,83 @@ trait Types extends api.Types { self: SymbolTable => val start = Statistics.pushTimer(typeOpsStack, findMemberNanos) //Console.println("find member " + name.decode + " in " + this + ":" + this.baseClasses)//DEBUG - var members: Scope = null var member: Symbol = NoSymbol + var members: List[Symbol] = null + var lastM: ::[Symbol] = null + var membertpe: Type = null + var required = requiredFlags var excluded = excludedFlags | DEFERRED var continue = true var self: Type = null - var membertpe: Type = null + val fingerPrint: Long = name.fingerPrint + while (continue) { continue = false val bcs0 = baseClasses var bcs = bcs0 while (!bcs.isEmpty) { val decls = bcs.head.info.decls - var entry = - if (name == nme.ANYNAME) decls.elems else decls.lookupEntry(name) - while (entry ne null) { - val sym = entry.sym - if (sym hasAllFlags requiredFlags) { - val excl = sym.getFlag(excluded) - if (excl == 0L && - (// omit PRIVATE LOCALS unless selector class is contained in class owning the def. - (bcs eq bcs0) || - !sym.isPrivateLocal || - (bcs0.head.hasTransOwner(bcs.head)))) { - if (name.isTypeName || stableOnly && sym.isStable) { - Statistics.popTimer(typeOpsStack, start) - if (suspension ne null) suspension foreach (_.suspended = false) - return sym - } else if (member == NoSymbol) { - member = sym - } else if (members eq null) { - if (member.name != sym.name || - !(member == sym || - member.owner != sym.owner && - !sym.isPrivate && { - if (self eq null) self = this.narrow - if (membertpe eq null) membertpe = self.memberType(member) - (membertpe matches self.memberType(sym)) - })) { - members = newScope - members enter member - members enter sym - } - } else { - var prevEntry = members.lookupEntry(sym.name) - var symtpe: Type = null - while ((prevEntry ne null) && - !(prevEntry.sym == sym || - prevEntry.sym.owner != sym.owner && - !sym.hasFlag(PRIVATE) && { - if (self eq null) self = this.narrow - if (symtpe eq null) symtpe = self.memberType(sym) - self.memberType(prevEntry.sym) matches symtpe - })) { - prevEntry = members lookupNextEntry prevEntry - } - if (prevEntry eq null) { - members enter sym + if ((fingerPrint & decls.fingerPrints) != 0) { + var entry = decls.lookupEntry(name) + while (entry ne null) { + val sym = entry.sym + val flags = sym.flags + if ((flags & required) == required) { + val excl = flags & excluded + if (excl == 0L && + (// omit PRIVATE LOCALS unless selector class is contained in class owning the def. + (bcs eq bcs0) || + (flags & PrivateLocal) != PrivateLocal || + (bcs0.head.hasTransOwner(bcs.head)))) { + if (name.isTypeName || stableOnly && sym.isStable) { + Statistics.popTimer(typeOpsStack, start) + if (suspension ne null) suspension foreach (_.suspended = false) + return sym + } else if (member eq NoSymbol) { + member = sym + } else if (members eq null) { + if ((member ne sym) && + ((member.owner eq sym.owner) || + (flags & PRIVATE) != 0 || { + if (self eq null) self = this.narrow + if (membertpe eq null) membertpe = self.memberType(member) + !(membertpe matches self.memberType(sym)) + })) { + lastM = new ::(sym, null) + members = member :: lastM + } + } else { + var others: List[Symbol] = members + var symtpe: Type = null + while ((others ne null) && { + val other = others.head + (other ne sym) && + ((other.owner eq sym.owner) || + (flags & PRIVATE) != 0 || { + if (self eq null) self = this.narrow + if (symtpe eq null) symtpe = self.memberType(sym) + !(self.memberType(other) matches symtpe) + })}) { + others = others.tail + } + if (others eq null) { + val lastM1 = new ::(sym, null) + lastM.tl = lastM1 + lastM = lastM1 + } } + } else if (excl == DEFERRED) { + continue = true } - } else if (excl == DEFERRED.toLong) { - continue = true } - } - entry = if (name == nme.ANYNAME) entry.next else decls lookupNextEntry entry - } // while (entry ne null) + entry = decls lookupNextEntry entry + } // while (entry ne null) + } // if (fingerPrint matches) // excluded = excluded | LOCAL bcs = if (name == nme.CONSTRUCTOR) Nil else bcs.tail } // while (!bcs.isEmpty) - excluded = excludedFlags + required |= DEFERRED + excluded &= ~(DEFERRED.toLong) } // while (continue) Statistics.popTimer(typeOpsStack, start) if (suspension ne null) suspension foreach (_.suspended = false) @@ -1117,9 +1193,11 @@ trait Types extends api.Types { self: SymbolTable => member } else { Statistics.incCounter(multMemberCount) - baseClasses.head.newOverloaded(this, members.toList) + lastM.tl = Nil + baseClasses.head.newOverloaded(this, members) } } + /** The (existential or otherwise) skolems and existentially quantified variables which are free in this type */ def skolemsExceptMethodTypeParams: List[Symbol] = { var boundSyms: List[Symbol] = List() @@ -1610,8 +1688,13 @@ trait Types extends api.Types { self: SymbolTable => if (period != currentPeriod) { tpe.baseClassesPeriod = currentPeriod if (!isValidForBaseClasses(period)) { - tpe.baseClassesCache = null - tpe.baseClassesCache = tpe.memo(computeBaseClasses)(tpe.typeSymbol :: _.baseClasses.tail) + val start = Statistics.pushTimer(typeOpsStack, baseClassesNanos) + try { + tpe.baseClassesCache = null + tpe.baseClassesCache = tpe.memo(computeBaseClasses)(tpe.typeSymbol :: _.baseClasses.tail) + } finally { + Statistics.popTimer(typeOpsStack, start) + } } } if (tpe.baseClassesCache eq null) @@ -5080,7 +5163,7 @@ trait Types extends api.Types { self: SymbolTable => */ def needsOuterTest(patType: Type, selType: Type, currentOwner: Symbol) = { def createDummyClone(pre: Type): Type = { - val dummy = currentOwner.enclClass.newValue(nme.ANYNAME).setInfo(pre.widen) + val dummy = currentOwner.enclClass.newValue(nme.ANYname).setInfo(pre.widen) singleType(ThisType(currentOwner.enclClass), dummy) } def maybeCreateDummyClone(pre: Type, sym: Symbol): Type = pre match { @@ -6923,14 +7006,17 @@ object TypesStats { val lubCount = Statistics.newCounter ("#toplevel lubs/glbs") val nestedLubCount = Statistics.newCounter ("#all lubs/glbs") val findMemberCount = Statistics.newCounter ("#findMember ops") + val findMembersCount = Statistics.newCounter ("#findMembers ops") val noMemberCount = Statistics.newSubCounter(" of which not found", findMemberCount) val multMemberCount = Statistics.newSubCounter(" of which multiple overloaded", findMemberCount) val typerNanos = Statistics.newTimer ("time spent typechecking", "typer") val lubNanos = Statistics.newStackableTimer("time spent in lubs", typerNanos) val subtypeNanos = Statistics.newStackableTimer("time spent in <:<", typerNanos) val findMemberNanos = Statistics.newStackableTimer("time spent in findmember", typerNanos) + val findMembersNanos = Statistics.newStackableTimer("time spent in findmembers", typerNanos) val asSeenFromNanos = Statistics.newStackableTimer("time spent in asSeenFrom", typerNanos) val baseTypeSeqNanos = Statistics.newStackableTimer("time spent in baseTypeSeq", typerNanos) + val baseClassesNanos = Statistics.newStackableTimer("time spent in baseClasses", typerNanos) val compoundBaseTypeSeqCount = Statistics.newSubCounter(" of which for compound types", baseTypeSeqCount) val typerefBaseTypeSeqCount = Statistics.newSubCounter(" of which for typerefs", baseTypeSeqCount) val singletonBaseTypeSeqCount = Statistics.newSubCounter(" of which for singletons", baseTypeSeqCount) 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/makro/Enclosures.scala b/src/reflect/scala/reflect/makro/Enclosures.scala index 69bd8d09c7..ff5c13a785 100644 --- a/src/reflect/scala/reflect/makro/Enclosures.scala +++ b/src/reflect/scala/reflect/makro/Enclosures.scala @@ -36,10 +36,6 @@ trait Enclosures { */ val enclosingPosition: Position - /** Tree that corresponds to the enclosing application, or EmptyTree if not applicable. - */ - val enclosingApplication: Tree - /** Tree that corresponds to the enclosing method, or EmptyTree if not applicable. */ val enclosingMethod: Tree diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 185621efa4..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? @@ -107,15 +119,28 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym // ----------- Implementations of mirror operations and classes ------------------- + private def ErrorInnerClass(wannabe: Symbol) = throw new ScalaReflectionException(s"$wannabe is an inner class, use reflectClass on an InstanceMirror to obtain its ClassMirror") + private def ErrorInnerModule(wannabe: Symbol) = throw new ScalaReflectionException(s"$wannabe is an inner module, use reflectModule on an InstanceMirror to obtain its ModuleMirror") + private def ErrorStaticClass(wannabe: Symbol) = throw new ScalaReflectionException(s"$wannabe is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror") + private def ErrorStaticModule(wannabe: Symbol) = throw new ScalaReflectionException(s"$wannabe is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror") + private def ErrorNotMember(wannabe: Symbol, owner: Symbol) = throw new ScalaReflectionException(s"expected a member of $owner, you provided ${wannabe.kind} ${wannabe.fullName}") + private def ErrorNotField(wannabe: Symbol) = throw new ScalaReflectionException(s"expected a field or an accessor method symbol, you provided $wannabe}") + private def ErrorNonExistentField(wannabe: Symbol) = throw new ScalaReflectionException(s""" + |Scala field ${wannabe.name} isn't represented as a Java field, neither it has a Java accessor method + |note that private parameters of class constructors don't get mapped onto fields and/or accessors, + |unless they are used outside of their declaring constructors. + """.trim.stripMargin) + private def ErrorSetImmutableField(wannabe: Symbol) = throw new ScalaReflectionException(s"cannot set an immutable field ${wannabe.name}") + def reflect(obj: Any): InstanceMirror = new JavaInstanceMirror(obj.asInstanceOf[AnyRef]) def reflectClass(cls: ClassSymbol): ClassMirror = { - if (!cls.isStatic) throw new Error("this is an inner class, use reflectClass on an InstanceMirror to obtain its ClassMirror") + if (!cls.isStatic) ErrorInnerClass(cls) new JavaClassMirror(null, cls) } def reflectModule(mod: ModuleSymbol): ModuleMirror = { - if (!mod.isStatic) throw new Error("this is an inner module, use reflectModule on an InstanceMirror to obtain its ModuleMirror") + if (!mod.isStatic) ErrorInnerModule(mod) new JavaModuleMirror(null, mod) } @@ -127,34 +152,39 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym def moduleSymbol(rtcls: RuntimeClass): ModuleSymbol = classToScala(rtcls).companionModule.asModuleSymbol + private def checkMemberOf(wannabe: Symbol, owner: Symbol) = + if (!owner.info.member(wannabe.name).alternatives.contains(wannabe)) ErrorNotMember(wannabe, owner) + private class JavaInstanceMirror(obj: AnyRef) extends InstanceMirror { def instance = obj def symbol = wholemirror.classSymbol(obj.getClass) def reflectField(field: TermSymbol): FieldMirror = { - // [Eugene+++] check whether `field` represents a member of a `symbol` - if (field.isMethod || field.isModule) throw new Error(s""" - |expected a field symbol, you provided a ${field.kind} symbol - |A typical cause of this problem is using a field accessor symbol instead of a field symbol. - |To obtain a field symbol append nme.LOCAL_SUFFIX_STRING to the name of the field, - |when searching for a member with Type.members or Type.declarations. - |This is a temporary inconvenience that will be resolved before 2.10.0-final. - |More information can be found here: https://issues.scala-lang.org/browse/SI-5895. - """.trim.stripMargin) - new JavaFieldMirror(obj, field) + checkMemberOf(field, symbol) + if ((field.isMethod && !field.isAccessor) || field.isModule) ErrorNotField(field) + val name = + if (field.isGetter) nme.getterToLocal(field.name) + else if (field.isSetter) nme.getterToLocal(nme.setterToGetter(field.name)) + else field.name + val field1 = (field.owner.info decl name).asTermSymbol + try fieldToJava(field1) + catch { + case _: NoSuchFieldException => ErrorNonExistentField(field1) + } + new JavaFieldMirror(obj, field1) } def reflectMethod(method: MethodSymbol): MethodMirror = { - // [Eugene+++] check whether `method` represents a member of a `symbol` + checkMemberOf(method, symbol) new JavaMethodMirror(obj, method) } def reflectClass(cls: ClassSymbol): ClassMirror = { - // [Eugene+++] check whether `cls` represents a member of a `symbol` - if (cls.isStatic) throw new Error("this is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror") + if (cls.isStatic) ErrorStaticClass(cls) + checkMemberOf(cls, symbol) new JavaClassMirror(instance, cls) } def reflectModule(mod: ModuleSymbol): ModuleMirror = { - // [Eugene+++] check whether `mod` represents a member of a `symbol` - if (mod.isStatic) throw new Error("this is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror") + if (mod.isStatic) ErrorStaticModule(mod) + checkMemberOf(mod, symbol) new JavaModuleMirror(instance, mod) } } @@ -168,7 +198,7 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym } def get = jfield.get(receiver) def set(value: Any) = { - if (!symbol.isMutable) throw new Error("cannot set an immutable field") + if (!symbol.isMutable) ErrorSetImmutableField(symbol) jfield.set(receiver, value) } } @@ -186,7 +216,7 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym case nme.length => jArray.getLength(receiver) case nme.apply => jArray.get(receiver, args(0).asInstanceOf[Int]) case nme.update => jArray.set(receiver, args(0).asInstanceOf[Int], args(1)) - case _ => throw new Error(s"unexpected array method $symbol") + case _ => assert(false, s"unexpected array method: $symbol") } else jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*) @@ -1001,7 +1031,7 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { self: Sym private lazy val magicSymbols: Map[(String, Name), Symbol] = { def mapEntry(sym: Symbol): ((String, Name), Symbol) = (sym.owner.fullName, sym.name) -> sym - Map() ++ (definitions.magicSymbols filter (_.isClass) map mapEntry) + Map() ++ (definitions.magicSymbols filter (_.isType) map mapEntry) } /** 1. If `owner` is a package class (but not the empty package) and `name` is a term name, make a new package diff --git a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala index 17e3d7581a..95c1bc861b 100644 --- a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala +++ b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala @@ -99,8 +99,10 @@ trait SymbolLoaders { self: SymbolTable => 0 < dp && dp < (name.length - 1) } - class PackageScope(pkgClass: Symbol) extends Scope() with SynchronizedScope { + class PackageScope(pkgClass: Symbol) extends Scope(initFingerPrints = -1L) // disable fingerprinting as we do not know entries beforehand + with SynchronizedScope { assert(pkgClass.isType) + // disable fingerprinting as we do not know entries beforehand private val negatives = mutable.Set[Name]() // Syncnote: Performance only, so need not be protected. override def lookupEntry(name: Name): ScopeEntry = { val e = super.lookupEntry(name) 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/future-spec/FutureTests.scala b/test/files/jvm/future-spec/FutureTests.scala index ca9ff5090f..30e1a722bf 100644 --- a/test/files/jvm/future-spec/FutureTests.scala +++ b/test/files/jvm/future-spec/FutureTests.scala @@ -507,6 +507,12 @@ object FutureTests extends MinimalScalaTest { } Await.ready(complex, defaultTimeout).isCompleted mustBe (true) } + + "should not throw when Await.ready" in { + val expected = try Right(5 / 0) catch { case a: ArithmeticException => Left(a) } + val f = future(5).map(_ / 0) + Await.ready(f, defaultTimeout).value.get.toString mustBe expected.toString + } } diff --git a/test/files/jvm/future-spec/PromiseTests.scala b/test/files/jvm/future-spec/PromiseTests.scala index 49bc642b57..d15bb31f36 100644 --- a/test/files/jvm/future-spec/PromiseTests.scala +++ b/test/files/jvm/future-spec/PromiseTests.scala @@ -78,7 +78,7 @@ object PromiseTests extends MinimalScalaTest { "contain a value" in { f((future, result) => future.value mustBe (Some(Right(result)))) } - "return result with 'blocking'" in { f((future, result) => blocking(future, defaultTimeout) mustBe (result)) } + "return when ready with 'Await.ready'" in { f((future, result) => Await.ready(future, defaultTimeout).isCompleted mustBe (true)) } "return result with 'Await.result'" in { f((future, result) => Await.result(future, defaultTimeout) mustBe (result)) } @@ -163,12 +163,9 @@ object PromiseTests extends MinimalScalaTest { }) } - "throw exception with 'blocking'" in { + "throw not throw exception with 'Await.ready'" in { f { - (future, message) => - intercept[E] { - blocking(future, defaultTimeout) - }.getMessage mustBe (message) + (future, message) => Await.ready(future, defaultTimeout).isCompleted mustBe (true) } } diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index 5c9c71f3f8..43d4c9dc71 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -4,7 +4,9 @@ import scala.concurrent.{ TimeoutException, SyncVar, ExecutionException, - ExecutionContext + ExecutionContext, + CanAwait, + Await } import scala.concurrent.{ future, promise, blocking } import scala.util.{ Try, Success, Failure } @@ -647,7 +649,7 @@ trait FutureProjections extends TestBase { val f = future { throw cause } - assert(blocking(f.failed, Duration(500, "ms")) == cause) + assert(Await.result(f.failed, Duration(500, "ms")) == cause) done() } @@ -655,7 +657,7 @@ trait FutureProjections extends TestBase { done => val f = future { 0 } try { - blocking(f.failed, Duration(500, "ms")) + Await.result(f.failed, Duration(500, "ms")) assert(false) } catch { case nsee: NoSuchElementException => done() @@ -678,7 +680,7 @@ trait Blocking extends TestBase { def testAwaitSuccess(): Unit = once { done => val f = future { 0 } - blocking(f, Duration(500, "ms")) + Await.result(f, Duration(500, "ms")) done() } @@ -689,7 +691,7 @@ trait Blocking extends TestBase { throw cause } try { - blocking(f, Duration(500, "ms")) + Await.result(f, Duration(500, "ms")) assert(false) } catch { case t => @@ -698,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 { @@ -708,7 +719,7 @@ trait BlockContexts extends TestBase { import scala.concurrent.{ Await, Awaitable, BlockContext } private def getBlockContext(body: => BlockContext): BlockContext = { - blocking(Future { body }, Duration(500, "ms")) + Await.result(Future { body }, Duration(500, "ms")) } // test outside of an ExecutionContext @@ -727,8 +738,7 @@ trait BlockContexts extends TestBase { def testPushCustom(): Unit = { val orig = BlockContext.current val customBC = new BlockContext() { - override def internalBlockingCall[T](awaitable: Awaitable[T], atMost: Duration): T = - orig.internalBlockingCall(awaitable, atMost) + override def blockOn[T](thunk: =>T)(implicit permission: CanAwait): T = orig.blockOn(thunk) } val bc = getBlockContext({ @@ -744,8 +754,7 @@ trait BlockContexts extends TestBase { def testPopCustom(): Unit = { val orig = BlockContext.current val customBC = new BlockContext() { - override def internalBlockingCall[T](awaitable: Awaitable[T], atMost: Duration): T = - orig.internalBlockingCall(awaitable, atMost) + override def blockOn[T](thunk: =>T)(implicit permission: CanAwait): T = orig.blockOn(thunk) } val bc = getBlockContext({ 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/t4425.check b/test/files/neg/t4425.check index 0f2fe6f2d1..a6a1a1fad4 100644 --- a/test/files/neg/t4425.check +++ b/test/files/neg/t4425.check @@ -1,4 +1,5 @@ -t4425.scala:3: error: isInstanceOf cannot test if value types are references. +t4425.scala:3: error: error during expansion of this match (this is a scalac bug). +The underlying error was: value _1 is not a member of object Foo.X 42 match { case _ X _ => () } - ^ + ^ one error 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/t5892.check b/test/files/neg/t5892.check new file mode 100644 index 0000000000..839bf9de23 --- /dev/null +++ b/test/files/neg/t5892.check @@ -0,0 +1,17 @@ +t5892.scala:5: error: type mismatch; + found : Boolean(false) + required: String +class C[@annot(false) X] { + ^ +t5892.scala:9: error: not found: value b2s +class D[@annot(b2s(false)) X] { + ^ +t5892.scala:13: error: type mismatch; + found : Boolean(false) + required: String +@annot(false) class E { + ^ +t5892.scala:17: error: not found: value b2s +@annot(b2s(false)) class F { + ^ +four errors found diff --git a/test/files/neg/t5892.scala b/test/files/neg/t5892.scala new file mode 100644 index 0000000000..5e3b2f313e --- /dev/null +++ b/test/files/neg/t5892.scala @@ -0,0 +1,25 @@ +import language.implicitConversions + +class annot(a: String) extends annotation.StaticAnnotation + +class C[@annot(false) X] { + implicit def b2s(b: Boolean): String = "" +} + +class D[@annot(b2s(false)) X] { + implicit def b2s(b: Boolean): String = "" +} + +@annot(false) class E { + implicit def b2s(b: Boolean): String = "" +} + +@annot(b2s(false)) class F { + implicit def b2s(b: Boolean): String = "" +} + +object T { + implicit def b2s(b: Boolean): String = "" + @annot(false) val x = 0 + @annot(b2s(false)) val y = 0 +} 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/t1832.scala b/test/files/pos/t1832.scala new file mode 100644 index 0000000000..c7b1ffb838 --- /dev/null +++ b/test/files/pos/t1832.scala @@ -0,0 +1,8 @@ +trait Cloning { + trait Foo + def fn(g: Any => Unit): Foo + + implicit def mkStar(i: Int) = new { def *(a: Foo): Foo = null } + + val pool = 4 * fn { case ghostSYMBOL: Int => ghostSYMBOL * 2 } +}
\ No newline at end of file diff --git a/test/files/pos/t5892.scala b/test/files/pos/t5892.scala new file mode 100644 index 0000000000..241e59860a --- /dev/null +++ b/test/files/pos/t5892.scala @@ -0,0 +1,5 @@ +class foo(a: String) extends annotation.StaticAnnotation +object o { + implicit def i2s(i: Int) = "" + @foo(1: String) def blerg { } +} 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/pos/t6047.flags b/test/files/pos/t6047.flags new file mode 100644 index 0000000000..cd66464f2f --- /dev/null +++ b/test/files/pos/t6047.flags @@ -0,0 +1 @@ +-language:experimental.macros
\ No newline at end of file diff --git a/test/files/pos/t6047.scala b/test/files/pos/t6047.scala new file mode 100644 index 0000000000..66b52b285f --- /dev/null +++ b/test/files/pos/t6047.scala @@ -0,0 +1,20 @@ +import scala.reflect.makro.Context +import java.io.InputStream + +object Macros { + def unpack[A](input: InputStream): A = macro unpack_impl[A] + + def unpack_impl[A: c.TypeTag](c: Context)(input: c.Expr[InputStream]): c.Expr[A] = { + import c.universe._ + + def unpackcode(tpe: c.Type): c.Expr[_] = { + if (tpe <:< implicitly[c.AbsTypeTag[Traversable[_]]].tpe) { + + } + ??? + } + + unpackcode(c.typeOf[A]) + ??? + } + }
\ No newline at end of file diff --git a/test/files/run/existentials-in-compiler.scala b/test/files/run/existentials-in-compiler.scala index a37005b1c4..14c25849cb 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 => exitingTyper { clazz.info println(clazz.defString) diff --git a/test/files/run/inline-ex-handlers.check b/test/files/run/inline-ex-handlers.check index 7d96c447b0..25e1b2a4dd 100644 --- a/test/files/run/inline-ex-handlers.check +++ b/test/files/run/inline-ex-handlers.check @@ -34,11 +34,11 @@ < 101 JUMP 4 < < 4: -512c517 +515c520 < blocks: [1,2,3,4,6,7,8,9,10] --- > blocks: [1,2,3,4,6,7,8,9,10,11,12,13] -541c546,551 +544c549,554 < 306 THROW(MyException) --- > ? JUMP 11 @@ -47,7 +47,7 @@ > ? LOAD_LOCAL(variable monitor4) > 305 MONITOR_EXIT > ? JUMP 12 -547c557,563 +550c560,566 < ? THROW(Throwable) --- > ? JUMP 12 @@ -57,7 +57,7 @@ > 304 MONITOR_EXIT > ? STORE_LOCAL(value t) > ? JUMP 13 -553c569,582 +556c572,585 < ? THROW(Throwable) --- > ? STORE_LOCAL(value t) @@ -74,19 +74,19 @@ > 310 CALL_PRIMITIVE(EndConcat) > 310 CALL_METHOD scala.Predef.println (dynamic) > 310 JUMP 2 -577c606 +580c609 < catch (Throwable) in ArrayBuffer(7, 8, 9, 10) starting at: 6 --- > catch (Throwable) in ArrayBuffer(7, 8, 9, 10, 11) starting at: 6 -580c609 +583c612 < catch (Throwable) in ArrayBuffer(4, 6, 7, 8, 9, 10) starting at: 3 --- > catch (Throwable) in ArrayBuffer(4, 6, 7, 8, 9, 10, 11, 12) starting at: 3 -612c641 +615c644 < blocks: [1,2,3,4,5,6,7,9,10] --- > blocks: [1,2,3,4,5,6,7,9,10,11,12] -636c665,671 +639c668,674 < 78 THROW(IllegalArgumentException) --- > ? STORE_LOCAL(value e) @@ -96,7 +96,7 @@ > 81 LOAD_LOCAL(value e) > ? STORE_LOCAL(variable exc1) > ? JUMP 12 -665c700,714 +668c703,717 < 81 THROW(Exception) --- > ? STORE_LOCAL(variable exc1) @@ -114,15 +114,15 @@ > 84 STORE_LOCAL(variable result) > 84 LOAD_LOCAL(variable exc1) > 84 THROW(Throwable) -687c736 +690c739 < catch (<none>) in ArrayBuffer(4, 6, 7, 9) starting at: 3 --- > catch (<none>) in ArrayBuffer(4, 6, 7, 9, 11) starting at: 3 -713c762 +716c765 < blocks: [1,2,3,4,5,6,9,12,14,17,18,19,22,25,27,28,30,31] --- > blocks: [1,2,3,4,5,6,9,12,14,17,18,19,22,25,27,28,30,31,32,33,34] -737c786,793 +740c789,796 < 172 THROW(MyException) --- > ? STORE_LOCAL(value ex6) @@ -133,12 +133,12 @@ > 170 STORE_LOCAL(value x4) > 170 SCOPE_ENTER value x4 > 170 JUMP 18 -793c849,850 +798c854,855 < 177 THROW(MyException) --- > ? STORE_LOCAL(value ex6) > ? JUMP 33 -797c854,861 +802c859,866 < 170 THROW(Throwable) --- > ? STORE_LOCAL(value ex6) @@ -149,17 +149,17 @@ > 169 STORE_LOCAL(value x4) > 169 SCOPE_ENTER value x4 > 169 JUMP 5 -830c894,895 +837c901,902 < 182 THROW(MyException) --- > ? STORE_LOCAL(variable exc2) > ? JUMP 34 -834c899,900 +841c906,907 < 169 THROW(Throwable) --- > ? STORE_LOCAL(variable exc2) > ? JUMP 34 -835a902,914 +842a909,921 > 34: > 184 LOAD_MODULE object Predef > 184 CONSTANT("finally") @@ -173,19 +173,19 @@ > 185 LOAD_LOCAL(variable exc2) > 185 THROW(Throwable) > -856c935 +863c942 < catch (Throwable) in ArrayBuffer(17, 18, 19, 22, 25, 27, 28, 30) starting at: 4 --- > catch (Throwable) in ArrayBuffer(17, 18, 19, 22, 25, 27, 28, 30, 32) starting at: 4 -859c938 +866c945 < catch (<none>) in ArrayBuffer(4, 5, 6, 9, 12, 17, 18, 19, 22, 25, 27, 28, 30) starting at: 3 --- > catch (<none>) in ArrayBuffer(4, 5, 6, 9, 12, 17, 18, 19, 22, 25, 27, 28, 30, 32, 33) starting at: 3 -885c964 +892c971 < blocks: [1,2,3,6,7,8,11,14,16,17,19] --- > blocks: [1,2,3,6,7,8,11,14,16,17,19,20] -909c988,995 +916c995,1002 < 124 THROW(MyException) --- > ? STORE_LOCAL(value ex6) @@ -196,15 +196,15 @@ > 122 STORE_LOCAL(value x4) > 122 SCOPE_ENTER value x4 > 122 JUMP 7 -969c1055 +979c1065 < catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 14, 16, 17, 19) starting at: 3 --- > catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 14, 16, 17, 19, 20) starting at: 3 -995c1081 +1005c1091 < blocks: [1,2,3,4,5,8,11,15,16,17,19] --- > blocks: [1,2,3,5,8,11,15,16,17,19,20] -1019c1105,1114 +1029c1115,1124 < 148 THROW(MyException) --- > ? STORE_LOCAL(value ex6) @@ -217,15 +217,15 @@ > 154 LOAD_LOCAL(value x4) > 154 IS_INSTANCE REF(class MyException) > 154 CZJUMP (BOOL)NE ? 5 : 11 -1040,1042d1134 +1050,1052d1144 < 145 JUMP 4 < < 4: -1275c1367 +1288c1380 < blocks: [1,2,3,4,5,7] --- > blocks: [1,2,3,4,5,7,8] -1299c1391,1398 +1312c1404,1411 < 38 THROW(IllegalArgumentException) --- > ? STORE_LOCAL(value e) @@ -236,16 +236,16 @@ > 42 CONSTANT("IllegalArgumentException") > 42 CALL_METHOD scala.Predef.println (dynamic) > 42 JUMP 2 -1348c1447 +1361c1460 < blocks: [1,2,3,4,5,8,11,13,14,16,17,19] --- > blocks: [1,2,3,5,8,11,13,14,16,17,19,20] -1372c1471,1472 +1385c1484,1485 < 203 THROW(MyException) --- > ? STORE_LOCAL(value ex6) > ? JUMP 20 -1392c1492,1501 +1405c1505,1514 < 209 THROW(MyException) --- > ? STORE_LOCAL(value ex6) @@ -258,15 +258,15 @@ > 212 LOAD_LOCAL(value x4) > 212 IS_INSTANCE REF(class MyException) > 212 CZJUMP (BOOL)NE ? 5 : 11 -1405,1407d1513 +1418,1420d1526 < 200 JUMP 4 < < 4: -1467c1573 +1483c1589 < blocks: [1,2,3,4,5,7] --- > blocks: [1,2,3,4,5,7,8] -1491c1597,1604 +1507c1613,1620 < 58 THROW(IllegalArgumentException) --- > ? STORE_LOCAL(value e) @@ -277,11 +277,11 @@ > 62 CONSTANT("RuntimeException") > 62 CALL_METHOD scala.Predef.println (dynamic) > 62 JUMP 2 -1540c1653 +1556c1669 < blocks: [1,2,3,4] --- > blocks: [1,2,3,4,5] -1560c1673,1678 +1576c1689,1694 < 229 THROW(MyException) --- > ? JUMP 5 @@ -290,19 +290,19 @@ > ? LOAD_LOCAL(variable monitor1) > 228 MONITOR_EXIT > 228 THROW(Throwable) -1566c1684 +1582c1700 < ? THROW(Throwable) --- > 228 THROW(Throwable) -1594c1712 +1610c1728 < locals: value args, variable result, variable monitor2, variable monitorResult1 --- > locals: value exception$1, value args, variable result, variable monitor2, variable monitorResult1 -1596c1714 +1612c1730 < blocks: [1,2,3,4] --- > blocks: [1,2,3,4,5] -1619c1737,1745 +1635c1753,1761 < 245 THROW(MyException) --- > ? STORE_LOCAL(value exception$1) @@ -314,7 +314,7 @@ > ? LOAD_LOCAL(variable monitor2) > 244 MONITOR_EXIT > 244 THROW(Throwable) -1625c1751 +1641c1767 < ? THROW(Throwable) --- > 244 THROW(Throwable) 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/nullable-lazyvals.check b/test/files/run/nullable-lazyvals.check new file mode 100644 index 0000000000..4db5783257 --- /dev/null +++ b/test/files/run/nullable-lazyvals.check @@ -0,0 +1,3 @@ + +param1: null +param2: null diff --git a/test/files/run/nullable-lazyvals.scala b/test/files/run/nullable-lazyvals.scala new file mode 100644 index 0000000000..c201e74e75 --- /dev/null +++ b/test/files/run/nullable-lazyvals.scala @@ -0,0 +1,36 @@ + +/** Test that call-by-name parameters are set to null if + * they are used only to initialize a lazy value, after the + * value has been initialized. + */ + +class Foo(param1: => Object, param2: => String) { + lazy val field1 = param1 + lazy val field2 = try param2 finally println("") +} + +object Test extends App { + val foo = new Foo(new Object, "abc") + + foo.field1 + foo.field2 + + for (f <- foo.getClass.getDeclaredFields) { + f.setAccessible(true) + if (f.getName.startsWith("param")) { + println("%s: %s".format(f.getName, f.get(foo))) + } + } + + // test that try-finally does not generated a liftedTry + // helper. This would already fail the first part of the test, + // but this check will help diganose it (if the single access to a + // private field does not happen directly in the lazy val, it won't + // be nulled). + for (f <- foo.getClass.getDeclaredMethods) { + f.setAccessible(true) + if (f.getName.startsWith("lifted")) { + println("not expected: %s".format(f)) + } + } +} 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/reflection-constructormirror-inner-badpath.check b/test/files/run/reflection-constructormirror-inner-badpath.check index 28b936eca1..2fb0610ad6 100644 --- a/test/files/run/reflection-constructormirror-inner-badpath.check +++ b/test/files/run/reflection-constructormirror-inner-badpath.check @@ -1,2 +1,2 @@ -this is an inner class, use reflectClass on an InstanceMirror to obtain its ClassMirror
+class R is an inner class, use reflectClass on an InstanceMirror to obtain its ClassMirror
()
diff --git a/test/files/run/reflection-constructormirror-nested-badpath.check b/test/files/run/reflection-constructormirror-nested-badpath.check index 9ceb603dc2..acd21df9c0 100644 --- a/test/files/run/reflection-constructormirror-nested-badpath.check +++ b/test/files/run/reflection-constructormirror-nested-badpath.check @@ -1,2 +1,2 @@ -this is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror
+class R is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror
()
diff --git a/test/files/run/reflection-constructormirror-toplevel-badpath.check b/test/files/run/reflection-constructormirror-toplevel-badpath.check index 9ceb603dc2..acd21df9c0 100644 --- a/test/files/run/reflection-constructormirror-toplevel-badpath.check +++ b/test/files/run/reflection-constructormirror-toplevel-badpath.check @@ -1,2 +1,2 @@ -this is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror
+class R is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror
()
diff --git a/test/files/run/reflection-fieldmirror-accessorsareokay.check b/test/files/run/reflection-fieldmirror-accessorsareokay.check new file mode 100644 index 0000000000..635dcd04ce --- /dev/null +++ b/test/files/run/reflection-fieldmirror-accessorsareokay.check @@ -0,0 +1,6 @@ +true
+42
+2
+true
+2
+2
diff --git a/test/files/run/reflection-fieldmirror-accessorsareokay.scala b/test/files/run/reflection-fieldmirror-accessorsareokay.scala new file mode 100644 index 0000000000..c586ce9bdd --- /dev/null +++ b/test/files/run/reflection-fieldmirror-accessorsareokay.scala @@ -0,0 +1,29 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} + +object Test extends App { + class A { + var x: Int = 42 + } + + val a = new A + + val im: InstanceMirror = cm.reflect(a) + val cs = im.symbol + + def test(f: Symbol) = { + try { + val fm: FieldMirror = im.reflectField(f.asTermSymbol) + println(fm.symbol.isVariable) + println(fm.get) + fm.set(2) + println(fm.get) + } catch { + case ex: Throwable => + println(ex.getMessage) + } + } + + test(cs.typeSignature.declaration(newTermName("x")).asTermSymbol) + test(cs.typeSignature.declaration(newTermName("x_$eq")).asTermSymbol) +} diff --git a/test/files/run/reflection-fieldmirror-ctorparam.check b/test/files/run/reflection-fieldmirror-ctorparam.check index 8b99a6f772..31f6491b14 100644 --- a/test/files/run/reflection-fieldmirror-ctorparam.check +++ b/test/files/run/reflection-fieldmirror-ctorparam.check @@ -1 +1,3 @@ -class java.lang.NoSuchFieldException: Test$A$$x
+class scala.ScalaReflectionException: Scala field x isn't represented as a Java field, neither it has a Java accessor method +note that private parameters of class constructors don't get mapped onto fields and/or accessors, +unless they are used outside of their declaring constructors.
diff --git a/test/files/run/reflection-fieldmirror-getsetval.check b/test/files/run/reflection-fieldmirror-getsetval.check index 707bbcccce..e1927f68d0 100644 --- a/test/files/run/reflection-fieldmirror-getsetval.check +++ b/test/files/run/reflection-fieldmirror-getsetval.check @@ -1,2 +1,2 @@ 42
-cannot set an immutable field
+cannot set an immutable field x
diff --git a/test/files/run/reflection-fieldmirror-sanitycheck.check b/test/files/run/reflection-fieldmirror-sanitycheck.check deleted file mode 100644 index e5134de4e3..0000000000 --- a/test/files/run/reflection-fieldmirror-sanitycheck.check +++ /dev/null @@ -1,6 +0,0 @@ -expected a field symbol, you provided a method symbol -A typical cause of this problem is using a field accessor symbol instead of a field symbol. -To obtain a field symbol append nme.LOCAL_SUFFIX_STRING to the name of the field, -when searching for a member with Type.members or Type.declarations. -This is a temporary inconvenience that will be resolved before 2.10.0-final. -More information can be found here: https://issues.scala-lang.org/browse/SI-5895.
diff --git a/test/files/run/reflection-fieldmirror-sanitycheck.scala b/test/files/run/reflection-fieldmirror-sanitycheck.scala deleted file mode 100644 index 6a992dd282..0000000000 --- a/test/files/run/reflection-fieldmirror-sanitycheck.scala +++ /dev/null @@ -1,22 +0,0 @@ -import scala.reflect.runtime.universe._ -import scala.reflect.runtime.{currentMirror => cm} - -object Test extends App { - class A { - var x: Int = 42 - } - - val a = new A - - val im: InstanceMirror = cm.reflect(a) - val cs = im.symbol - //val f = cs.typeSignature.declaration(newTermName("x" + nme.LOCAL_SUFFIX_STRING)).asTermSymbol - val f = cs.typeSignature.declaration(newTermName("x")).asTermSymbol - try { - val fm: FieldMirror = im.reflectField(f) - println("this indicates a failure") - } catch { - case ex: Throwable => - println(ex.getMessage) - } -} diff --git a/test/files/run/reflection-magicsymbols-repl.check b/test/files/run/reflection-magicsymbols-repl.check new file mode 100644 index 0000000000..d2ef4ad3cd --- /dev/null +++ b/test/files/run/reflection-magicsymbols-repl.check @@ -0,0 +1,39 @@ +Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala>
+
+scala> import scala.reflect.runtime.universe._
+import scala.reflect.runtime.universe._
+
+scala> class A {
+ def foo1(x: Int*) = ???
+ def foo2(x: => Int) = ???
+ def foo3(x: Any) = ???
+ def foo4(x: AnyRef) = ???
+ def foo5(x: AnyVal) = ???
+ def foo6(x: Null) = ???
+ def foo7(x: Nothing) = ???
+ def foo8(x: Singleton) = ???
+}
+defined class A
+
+scala> def test(n: Int): Unit = {
+ val sig = typeOf[A] member newTermName("foo" + n) typeSignature
+ val x = sig.asInstanceOf[MethodType].params.head
+ println(x.typeSignature)
+}
+warning: there were 1 feature warnings; re-run with -feature for details
+test: (n: Int)Unit
+
+scala> for (i <- 1 to 8) test(i)
+scala.Int*
+=> scala.Int
+scala.Any
+scala.AnyRef
+scala.AnyVal
+scala.Null
+scala.Nothing
+scala.Singleton
+
+scala>
diff --git a/test/files/run/reflection-magicsymbols-repl.scala b/test/files/run/reflection-magicsymbols-repl.scala new file mode 100644 index 0000000000..26127b8661 --- /dev/null +++ b/test/files/run/reflection-magicsymbols-repl.scala @@ -0,0 +1,23 @@ +import scala.tools.partest.ReplTest + +object Test extends ReplTest { + def code = """ + |import scala.reflect.runtime.universe._ + |class A { + | def foo1(x: Int*) = ??? + | def foo2(x: => Int) = ??? + | def foo3(x: Any) = ??? + | def foo4(x: AnyRef) = ??? + | def foo5(x: AnyVal) = ??? + | def foo6(x: Null) = ??? + | def foo7(x: Nothing) = ??? + | def foo8(x: Singleton) = ??? + |} + |def test(n: Int): Unit = { + | val sig = typeOf[A] member newTermName("foo" + n) typeSignature + | val x = sig.asInstanceOf[MethodType].params.head + | println(x.typeSignature) + |} + |for (i <- 1 to 8) test(i) + |""".stripMargin +} diff --git a/test/files/run/reflection-magicsymbols-vanilla.check b/test/files/run/reflection-magicsymbols-vanilla.check new file mode 100644 index 0000000000..4f4e8d94a9 --- /dev/null +++ b/test/files/run/reflection-magicsymbols-vanilla.check @@ -0,0 +1,8 @@ +Int*
+=> Int
+Any
+AnyRef
+AnyVal
+Null
+Nothing
+Singleton
diff --git a/test/files/run/reflection-magicsymbols-vanilla.scala b/test/files/run/reflection-magicsymbols-vanilla.scala new file mode 100644 index 0000000000..32819dcc46 --- /dev/null +++ b/test/files/run/reflection-magicsymbols-vanilla.scala @@ -0,0 +1,20 @@ +class A { + def foo1(x: Int*) = ??? + def foo2(x: => Int) = ??? + def foo3(x: Any) = ??? + def foo4(x: AnyRef) = ??? + def foo5(x: AnyVal) = ??? + def foo6(x: Null) = ??? + def foo7(x: Nothing) = ??? + def foo8(x: Singleton) = ??? +} + +object Test extends App { + import scala.reflect.runtime.universe._ + def test(n: Int): Unit = { + val sig = typeOf[A] member newTermName("foo" + n) typeSignature + val x = sig.asInstanceOf[MethodType].params.head + println(x.typeSignature) + } + for (i <- 1 to 8) test(i) +} diff --git a/test/files/run/reflection-magicsymbols.check b/test/files/run/reflection-magicsymbols.check deleted file mode 100644 index 2600847d99..0000000000 --- a/test/files/run/reflection-magicsymbols.check +++ /dev/null @@ -1,22 +0,0 @@ -Type in expressions to have them evaluated.
-Type :help for more information.
-
-scala>
-
-scala> import scala.reflect.runtime.universe._
-import scala.reflect.runtime.universe._
-
-scala> class A { def foo(x: Int*) = 1 }
-defined class A
-
-scala> val sig = typeOf[A] member newTermName("foo") typeSignature
-warning: there were 1 feature warnings; re-run with -feature for details
-sig: reflect.runtime.universe.Type = (x: <?>)scala.Int
-
-scala> val x = sig.asInstanceOf[MethodType].params.head
-x: reflect.runtime.universe.Symbol = value x
-
-scala> println(x.typeSignature)
-scala.Int*
-
-scala>
diff --git a/test/files/run/reflection-magicsymbols.scala b/test/files/run/reflection-magicsymbols.scala deleted file mode 100644 index a40845d6ac..0000000000 --- a/test/files/run/reflection-magicsymbols.scala +++ /dev/null @@ -1,11 +0,0 @@ -import scala.tools.partest.ReplTest - -object Test extends ReplTest { - def code = """ - |import scala.reflect.runtime.universe._ - |class A { def foo(x: Int*) = 1 } - |val sig = typeOf[A] member newTermName("foo") typeSignature - |val x = sig.asInstanceOf[MethodType].params.head - |println(x.typeSignature) - |""".stripMargin -} diff --git a/test/files/run/reflection-modulemirror-inner-badpath.check b/test/files/run/reflection-modulemirror-inner-badpath.check index d3fe43336e..1e990ec900 100644 --- a/test/files/run/reflection-modulemirror-inner-badpath.check +++ b/test/files/run/reflection-modulemirror-inner-badpath.check @@ -1,2 +1,2 @@ -this is an inner module, use reflectModule on an InstanceMirror to obtain its ModuleMirror -() +object R is an inner module, use reflectModule on an InstanceMirror to obtain its ModuleMirror
+()
diff --git a/test/files/run/reflection-modulemirror-nested-badpath.check b/test/files/run/reflection-modulemirror-nested-badpath.check index 16a5b10390..f7980b9986 100644 --- a/test/files/run/reflection-modulemirror-nested-badpath.check +++ b/test/files/run/reflection-modulemirror-nested-badpath.check @@ -1,2 +1,2 @@ -this is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror -() +object R is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror
+()
diff --git a/test/files/run/reflection-modulemirror-toplevel-badpath.check b/test/files/run/reflection-modulemirror-toplevel-badpath.check index 16a5b10390..f7980b9986 100644 --- a/test/files/run/reflection-modulemirror-toplevel-badpath.check +++ b/test/files/run/reflection-modulemirror-toplevel-badpath.check @@ -1,2 +1,2 @@ -this is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror -() +object R is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror
+()
diff --git a/test/files/run/reflection-sanitychecks.check b/test/files/run/reflection-sanitychecks.check new file mode 100644 index 0000000000..d977e0ed66 --- /dev/null +++ b/test/files/run/reflection-sanitychecks.check @@ -0,0 +1,8 @@ +field: 1
+method: 2
+class: CC
+object: java.lang.Error: inner and nested modules are not supported yet
+field: scala.ScalaReflectionException: expected a member of class C, you provided value D.foo
+method: scala.ScalaReflectionException: expected a member of class C, you provided method D.bar
+class: scala.ScalaReflectionException: expected a member of class C, you provided class D.C
+object: scala.ScalaReflectionException: expected a member of class C, you provided object D.O
diff --git a/test/files/run/reflection-sanitychecks.scala b/test/files/run/reflection-sanitychecks.scala new file mode 100644 index 0000000000..a6a24088a4 --- /dev/null +++ b/test/files/run/reflection-sanitychecks.scala @@ -0,0 +1,30 @@ +class C { + val foo = 1 + def bar = 2 + class C { override def toString = "CC" } + object O { override def toString = "CO" } +} + +class D { + val foo = 3 + def bar = 4 + class C { override def toString = "DC" } + object O { override def toString = "DO" } +} + +object Test extends App { + import scala.reflect.runtime.universe._ + import scala.reflect.runtime.{currentMirror => cm} + val im = cm.reflect(new C) + + def test(tpe: Type): Unit = { + def failsafe(action: => Any): Any = try action catch { case ex: Throwable => ex.toString } + println("field: " + failsafe(im.reflectField(tpe.member(newTermName("foo")).asTermSymbol).get)) + println("method: " + failsafe(im.reflectMethod(tpe.member(newTermName("bar")).asMethodSymbol)())) + println("class: " + failsafe(im.reflectClass(tpe.member(newTypeName("C")).asClassSymbol).reflectConstructor(typeOf[C].member(newTypeName("C")).asClassSymbol.typeSignature.member(newTermName("<init>")).asMethodSymbol)())) + println("object: " + failsafe(im.reflectModule(tpe.member(newTermName("O")).asModuleSymbol).instance)) + } + + test(typeOf[C]) + test(typeOf[D]) +}
\ No newline at end of file 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/t4897.check b/test/files/run/t4897.check new file mode 100644 index 0000000000..17dda56fe1 --- /dev/null +++ b/test/files/run/t4897.check @@ -0,0 +1 @@ +joepie diff --git a/test/files/run/t4897.scala b/test/files/run/t4897.scala new file mode 100644 index 0000000000..a2ec3de37f --- /dev/null +++ b/test/files/run/t4897.scala @@ -0,0 +1,10 @@ +class CSuper { + object A +} +class C extends CSuper { + def f = (A: AnyRef) match { case _: A.type => "joepie" } +} + +object Test extends C with App { + println(f) +}
\ No newline at end of file diff --git a/test/files/run/t5158.check b/test/files/run/t5158.check new file mode 100644 index 0000000000..573541ac97 --- /dev/null +++ b/test/files/run/t5158.check @@ -0,0 +1 @@ +0 diff --git a/test/files/run/t5158.scala b/test/files/run/t5158.scala new file mode 100644 index 0000000000..3028ffa9e0 --- /dev/null +++ b/test/files/run/t5158.scala @@ -0,0 +1,17 @@ +case class B(var x: Int) { + def succ() { + x = x + 1 + } +} + +object Test { + def main(args: Array[String]) { + val b = B(0) + b match { + case B(x) => + //println(x) + b.succ() + println(x) + } + } +}
\ No newline at end of file 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/t5937.scala b/test/files/run/t5937.scala new file mode 100644 index 0000000000..e5bf6617af --- /dev/null +++ b/test/files/run/t5937.scala @@ -0,0 +1,12 @@ + + + +import collection._ + + + +object Test extends App { + + val list: List[Int] = (immutable.Vector(1, 2, 3) :+ 4)(breakOut) + +} diff --git a/test/files/run/t6070.check b/test/files/run/t6070.check new file mode 100644 index 0000000000..00750edc07 --- /dev/null +++ b/test/files/run/t6070.check @@ -0,0 +1 @@ +3 diff --git a/test/files/run/t6070.scala b/test/files/run/t6070.scala new file mode 100644 index 0000000000..b6af48ef21 --- /dev/null +++ b/test/files/run/t6070.scala @@ -0,0 +1,36 @@ +abstract class Bomb { + type T + val x: T + + def size(that: T): Int +} + +class StringBomb extends Bomb { + type T = String + val x = "abc" + def size(that: String): Int = that.length +} + +class IntBomb extends Bomb { + type T = Int + val x = 10 + + def size(that: Int) = x + that +} + +case class Mean(var bomb: Bomb) + +object Test extends App { + def foo(x: Mean) = x match { + case Mean(b) => + // BUG: b is assumed to be a stable identifier, but it can actually be mutated + println(b.size({ mutate(); b.x })) + } + + def mutate() { + m.bomb = new IntBomb + } + + val m = Mean(new StringBomb) + foo(m) // should print 3 +}
\ No newline at end of file diff --git a/test/files/run/t6086-repl.check b/test/files/run/t6086-repl.check new file mode 100644 index 0000000000..f868aa18d0 --- /dev/null +++ b/test/files/run/t6086-repl.check @@ -0,0 +1,12 @@ +Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala>
+
+scala> case class X(s: String)
+defined class X
+
+scala> scala.reflect.runtime.universe.typeOf[X]
+res0: reflect.runtime.universe.Type = X
+
+scala>
diff --git a/test/files/run/t6086-repl.scala b/test/files/run/t6086-repl.scala new file mode 100644 index 0000000000..87f94ec9f6 --- /dev/null +++ b/test/files/run/t6086-repl.scala @@ -0,0 +1,8 @@ +import scala.tools.partest.ReplTest + +object Test extends ReplTest { + def code = """ + |case class X(s: String) + |scala.reflect.runtime.universe.typeOf[X] + |""".stripMargin +} diff --git a/test/files/run/t6086-vanilla.check b/test/files/run/t6086-vanilla.check new file mode 100644 index 0000000000..fd66be08d0 --- /dev/null +++ b/test/files/run/t6086-vanilla.check @@ -0,0 +1 @@ +X
diff --git a/test/files/run/t6086-vanilla.scala b/test/files/run/t6086-vanilla.scala new file mode 100644 index 0000000000..b4de581ad5 --- /dev/null +++ b/test/files/run/t6086-vanilla.scala @@ -0,0 +1,6 @@ +case class X(s: String) + +object Test extends App { + import scala.reflect.runtime.universe._ + println(typeOf[X]) +}
\ No newline at end of file diff --git a/test/files/run/t6089.check b/test/files/run/t6089.check new file mode 100644 index 0000000000..a8d4424106 --- /dev/null +++ b/test/files/run/t6089.check @@ -0,0 +1 @@ +scala.MatchError: Foo(0) (of class Foo) diff --git a/test/files/run/t6089.scala b/test/files/run/t6089.scala new file mode 100644 index 0000000000..c72d7ba792 --- /dev/null +++ b/test/files/run/t6089.scala @@ -0,0 +1,13 @@ +case class Foo(x: Int) + +object Test { + def bippo(result: Boolean): Boolean = result + def bungus(m: Foo): Boolean = + bippo(m match { case Foo(2) => bungus(m) }) + + def main(args: Array[String]): Unit = try { + bungus(Foo(0)) + } catch { + case x: MatchError => println(x) + } +}
\ 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/pending/run/t5695.check b/test/pending/run/t5695.check deleted file mode 100644 index d50069ab4f..0000000000 --- a/test/pending/run/t5695.check +++ /dev/null @@ -1,2 +0,0 @@ -.. -.. diff --git a/test/pending/run/t5695/part_1.scala b/test/pending/run/t5695/part_1.scala deleted file mode 100644 index b8e8f8e52f..0000000000 --- a/test/pending/run/t5695/part_1.scala +++ /dev/null @@ -1,12 +0,0 @@ -import language.experimental.macros -import scala.reflect.makro.Context - -object Defs { - - def mkInt = macro mkIntImpl - def mkIntImpl(c: Context): c.Expr[Any] = { - println(c.enclosingApplication) - c.reify{ 23 } - } - -} diff --git a/test/pending/run/t5695/part_2.scala b/test/pending/run/t5695/part_2.scala deleted file mode 100644 index d34219437d..0000000000 --- a/test/pending/run/t5695/part_2.scala +++ /dev/null @@ -1,8 +0,0 @@ -import Defs._ - -object Test extends App { - - val i1 = mkInt - val i2 = identity(mkInt) - -} 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 + } + } } |