Permalink
Browse files

Scaladoc feature that shows implicit conversions

See https://github.com/VladUreche/scala/tree/feature/doc-implicits for the history.
See https://scala-webapps.epfl.ch/jenkins/view/scaladoc/job/scaladoc-implicits-nightly/ for nightlies.

Many thanks fly out to Adriaan for his help with implicit search!
  • Loading branch information...
VladUreche committed Apr 13, 2012
1 parent 821229f commit 355264f9d53c09182fe6f480319543dc914860d1
Showing with 2,001 additions and 263 deletions.
  1. +73 −67 build.xml
  2. +23 −16 src/compiler/scala/reflect/internal/Types.scala
  3. +64 −6 src/compiler/scala/tools/ant/Scaladoc.scala
  4. +4 −4 src/compiler/scala/tools/nsc/ast/DocComments.scala
  5. +4 −2 src/compiler/scala/tools/nsc/doc/DocFactory.scala
  6. +126 −1 src/compiler/scala/tools/nsc/doc/Settings.scala
  7. +2 −2 src/compiler/scala/tools/nsc/doc/Uncompilable.scala
  8. +3 −0 src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala
  9. +6 −6 src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala
  10. +130 −26 src/compiler/scala/tools/nsc/doc/html/page/Template.scala
  11. BIN src/compiler/scala/tools/nsc/doc/html/resource/lib/conversionbg.gif
  12. BIN src/compiler/scala/tools/nsc/doc/html/resource/lib/selected-implicits.png
  13. BIN src/compiler/scala/tools/nsc/doc/html/resource/lib/selected-right-implicits.png
  14. +79 −22 src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css
  15. +74 −30 src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js
  16. +107 −0 src/compiler/scala/tools/nsc/doc/model/Entity.scala
  17. +59 −40 src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
  18. +501 −0 src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala
  19. +1 −1 src/compiler/scala/tools/nsc/doc/model/TreeFactory.scala
  20. +44 −3 src/compiler/scala/tools/nsc/typechecker/Implicits.scala
  21. +1 −1 src/compiler/scala/tools/nsc/typechecker/Infer.scala
  22. +13 −0 src/library/scala/Array.scala
  23. +11 −0 src/library/scala/Option.scala
  24. +11 −1 src/library/scala/Tuple2.scala
  25. +12 −1 src/library/scala/Tuple3.scala
  26. +50 −28 src/partest/scala/tools/partest/ScaladocModelTest.scala
  27. +143 −0 test/scaladoc/resources/implicits-base-res.scala
  28. +48 −0 test/scaladoc/resources/implicits-chaining-res.scala
  29. +9 −0 test/scaladoc/resources/implicits-elimination-res.scala
  30. +51 −0 test/scaladoc/resources/implicits-scopes-res.scala
  31. +1 −1 test/scaladoc/run/SI-5373.check
  32. +3 −3 test/scaladoc/run/SI-5373.scala
  33. +1 −0 test/scaladoc/run/implicits-base.check
  34. +179 −0 test/scaladoc/run/implicits-base.scala
  35. +1 −0 test/scaladoc/run/implicits-chaining.check
  36. +64 −0 test/scaladoc/run/implicits-chaining.scala
  37. +1 −0 test/scaladoc/run/implicits-elimination.check
  38. +22 −0 test/scaladoc/run/implicits-elimination.scala
  39. +1 −0 test/scaladoc/run/implicits-scopes.check
  40. +76 −0 test/scaladoc/run/implicits-scopes.scala
  41. +3 −2 test/scaladoc/scalacheck/CommentFactoryTest.scala
View
140 build.xml

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -917,8 +917,8 @@ trait Types extends api.Types { self: SymbolTable =>
/** A test whether a type contains any unification type variables. */
def isGround: Boolean = this match {
- case TypeVar(_, constr) =>
- constr.instValid && constr.inst.isGround
+ case tv@TypeVar(_, _) =>
+ tv.untouchable || (tv.instValid && tv.constr.inst.isGround)
case TypeRef(pre, sym, args) =>
sym.isPackageClass || pre.isGround && (args forall (_.isGround))
case SingleType(pre, sym) =>
@@ -2677,14 +2677,15 @@ trait Types extends api.Types { self: SymbolTable =>
def unapply(tv: TypeVar): Some[(Type, TypeConstraint)] = Some((tv.origin, tv.constr))
def apply(origin: Type, constr: TypeConstraint): TypeVar = apply(origin, constr, Nil, Nil)
def apply(tparam: Symbol): TypeVar = apply(tparam.tpeHK, deriveConstraint(tparam), Nil, tparam.typeParams)
+ def apply(tparam: Symbol, untouchable: Boolean): TypeVar = apply(tparam.tpeHK, deriveConstraint(tparam), Nil, tparam.typeParams, untouchable)
/** This is the only place TypeVars should be instantiated.
*/
- def apply(origin: Type, constr: TypeConstraint, args: List[Type], params: List[Symbol]): TypeVar = {
+ def apply(origin: Type, constr: TypeConstraint, args: List[Type], params: List[Symbol], untouchable: Boolean = false): TypeVar = {
val tv = (
- if (args.isEmpty && params.isEmpty) new TypeVar(origin, constr)
- else if (args.size == params.size) new AppliedTypeVar(origin, constr, params zip args)
- else if (args.isEmpty) new HKTypeVar(origin, constr, params)
+ if (args.isEmpty && params.isEmpty) new TypeVar(origin, constr, untouchable)
+ else if (args.size == params.size) new AppliedTypeVar(origin, constr, untouchable, params zip args)
+ else if (args.isEmpty) new HKTypeVar(origin, constr, untouchable, params)
else throw new Error("Invalid TypeVar construction: " + ((origin, constr, args, params)))
)
@@ -2712,8 +2713,9 @@ trait Types extends api.Types { self: SymbolTable =>
class HKTypeVar(
_origin: Type,
_constr: TypeConstraint,
+ _untouchable: Boolean,
override val params: List[Symbol]
- ) extends TypeVar(_origin, _constr) {
+ ) extends TypeVar(_origin, _constr, _untouchable) {
require(params.nonEmpty, this)
override def isHigherKinded = true
@@ -2725,8 +2727,9 @@ trait Types extends api.Types { self: SymbolTable =>
class AppliedTypeVar(
_origin: Type,
_constr: TypeConstraint,
+ _untouchable: Boolean,
zippedArgs: List[(Symbol, Type)]
- ) extends TypeVar(_origin, _constr) {
+ ) extends TypeVar(_origin, _constr, _untouchable) {
require(zippedArgs.nonEmpty, this)
@@ -2749,7 +2752,8 @@ trait Types extends api.Types { self: SymbolTable =>
*/
class TypeVar(
val origin: Type,
- val constr0: TypeConstraint
+ val constr0: TypeConstraint,
+ val untouchable: Boolean = false // by other typevars
) extends Type {
override def params: List[Symbol] = Nil
override def typeArgs: List[Type] = Nil
@@ -2931,14 +2935,15 @@ trait Types extends api.Types { self: SymbolTable =>
// would be pointless. In this case, each check we perform causes us to lose specificity: in
// the end the best we'll do is the least specific type we tested against, since the typevar
// does not see these checks as "probes" but as requirements to fulfill.
- // TODO: the `suspended` flag can be used to poke around with leaving a trace
+ // TODO: can the `suspended` flag be used to poke around without leaving a trace?
//
// So the strategy used here is to test first the type, then the direct parents, and finally
// to fall back on the individual base types. This warrants eventual re-examination.
// AM: I think we could use the `suspended` flag to avoid side-effecting during unification
- if (suspended) // constraint accumulation is disabled
+ if (tp.isInstanceOf[TypeVar] && untouchable && !tp.asInstanceOf[TypeVar].untouchable) tp.asInstanceOf[TypeVar].registerBound(this, !isLowerBound, isNumericBound)
+ else if (suspended) // constraint accumulation is disabled
checkSubtype(tp, origin)
else if (constr.instValid) // type var is already set
checkSubtype(tp, constr.inst)
@@ -2962,7 +2967,8 @@ trait Types extends api.Types { self: SymbolTable =>
if(typeVarLHS) constr.inst =:= tp
else tp =:= constr.inst
- if (suspended) tp =:= origin
+ if (tp.isInstanceOf[TypeVar] && untouchable && !tp.asInstanceOf[TypeVar].untouchable) tp.asInstanceOf[TypeVar].registerTypeEquality(this, !typeVarLHS)
+ else if (suspended) tp =:= origin
else if (constr.instValid) checkIsSameType(tp)
else isRelatable(tp) && {
val newInst = wildcardToTypeVarMap(tp)
@@ -3036,7 +3042,7 @@ trait Types extends api.Types { self: SymbolTable =>
override def safeToString = (
if ((constr eq null) || (constr.inst eq null)) "TVar<" + originName + "=null>"
else if (constr.inst ne NoType) "" + constr.inst
- else "?" + levelString + originName
+ else (if(untouchable) "!?" else "?") + levelString + originName
)
override def kind = "TypeVar"
@@ -4733,7 +4739,7 @@ trait Types extends api.Types { self: SymbolTable =>
val sym1 = adaptToNewRun(sym.owner.thisType, sym)
if (sym1 == sym) tp else ThisType(sym1)
} catch {
- case ex: MissingTypeControl =>
+ case ex: MissingTypeControl =>
tp
}
case SingleType(pre, sym) =>
@@ -6044,8 +6050,9 @@ trait Types extends api.Types { self: SymbolTable =>
def stripType(tp: Type) = tp match {
case ExistentialType(_, res) =>
res
- case TypeVar(_, constr) =>
- if (constr.instValid) constr.inst
+ case tv@TypeVar(_, constr) =>
+ if (tv.instValid) constr.inst
+ else if (tv.untouchable) tv
else abort("trying to do lub/glb of typevar "+tp)
case t => t
}
@@ -75,6 +75,11 @@ class Scaladoc extends ScalaMatchingTask {
*/
object Flag extends PermissibleValue {
val values = List("yes", "no", "on", "off")
+ def getBooleanValue(value: String, flagName: String): Boolean =
+ if (Flag.isPermissible(value))
+ return ("yes".equals(value) || "on".equals(value))
+ else
+ buildError("Unknown " + flagName + " flag '" + value + "'")
}
/** The directories that contain source files to compile. */
@@ -127,6 +132,25 @@ class Scaladoc extends ScalaMatchingTask {
/** Instruct the ant task not to fail in the event of errors */
private var nofail: Boolean = false
+ /** Instruct the scaladoc tool to document implicit conversions */
+ private var docImplicits: Boolean = false
+
+ /** Instruct the scaladoc tool to document all (including impossible) implicit conversions */
+ private var docImplicitsShowAll: Boolean = false
+
+ /** Instruct the scaladoc tool to output implicits debugging information */
+ private var docImplicitsDebug: Boolean = false
+
+ /** Instruct the scaladoc tool to create diagrams */
+ private var docDiagrams: Boolean = false
+
+ /** Instruct the scaladoc tool to output diagram creation debugging information */
+ private var docDiagramsDebug: Boolean = false
+
+ /** Instruct the scaladoc tool to use the binary given to create diagrams */
+ private var docDiagramsDotPath: Option[String] = None
+
+
/*============================================================================*\
** Properties setters **
\*============================================================================*/
@@ -361,12 +385,39 @@ class Scaladoc extends ScalaMatchingTask {
*
* @param input One of the flags `yes/no` or `on/off`. Default if no/off.
*/
- def setNoFail(input: String) {
- if (Flag.isPermissible(input))
- nofail = "yes".equals(input) || "on".equals(input)
- else
- buildError("Unknown nofail flag '" + input + "'")
- }
+ def setNoFail(input: String) =
+ nofail = Flag.getBooleanValue(input, "nofail")
+
+ /** Set the `implicits` info attribute.
+ * @param input One of the flags `yes/no` or `on/off`. Default if no/off. */
+ def setImplicits(input: String) =
+ docImplicits = Flag.getBooleanValue(input, "implicits")
+
+ /** Set the `implicitsShowAll` info attribute to enable scaladoc to show all implicits, including those impossible to
+ * convert to from the default scope
+ * @param input One of the flags `yes/no` or `on/off`. Default if no/off. */
+ def setImplicitsShowAll(input: String) =
+ docImplicitsShowAll = Flag.getBooleanValue(input, "implicitsShowAll")
+
+ /** Set the `implicitsDebug` info attribute so scaladoc outputs implicit conversion debug information
+ * @param input One of the flags `yes/no` or `on/off`. Default if no/off. */
+ def setImplicitsDebug(input: String) =
+ docImplicitsDebug = Flag.getBooleanValue(input, "implicitsDebug")
+
+ /** Set the `diagrams` bit so Scaladoc adds diagrams to the documentation
+ * @param input One of the flags `yes/no` or `on/off`. Default if no/off. */
+ def setDiagrams(input: String) =
+ docDiagrams = Flag.getBooleanValue(input, "diagrams")
+
+ /** Set the `diagramsDebug` bit so Scaladoc outputs diagram building debug information
+ * @param input One of the flags `yes/no` or `on/off`. Default if no/off. */
+ def setDiagramsDebug(input: String) =
+ docDiagramsDebug = Flag.getBooleanValue(input, "diagramsDebug")
+
+ /** Set the `diagramsDotPath` attribute to the path where graphviz dot can be found (including the binary file name,
+ * eg: /usr/bin/dot) */
+ def setDiagramsDotPath(input: String) =
+ docDiagramsDotPath = Some(input)
/*============================================================================*\
** Properties getters **
@@ -560,6 +611,13 @@ class Scaladoc extends ScalaMatchingTask {
docSettings.deprecation.value = deprecation
docSettings.unchecked.value = unchecked
+ docSettings.docImplicits.value = docImplicits
+ docSettings.docImplicitsDebug.value = docImplicitsDebug
+ docSettings.docImplicitsShowAll.value = docImplicitsShowAll
+ docSettings.docDiagrams.value = docDiagrams
+ docSettings.docDiagramsDebug.value = docDiagramsDebug
+ if(!docDiagramsDotPath.isEmpty) docSettings.docDiagramsDotPath.value = docDiagramsDotPath.get
+
if (!docgenerator.isEmpty) docSettings.docgenerator.value = docgenerator.get
if (!docrootcontent.isEmpty) docSettings.docRootContent.value = docrootcontent.get.getAbsolutePath()
log("Scaladoc params = '" + addParams + "'", Project.MSG_DEBUG)
@@ -252,7 +252,7 @@ trait DocComments { self: Global =>
def replaceInheritdoc(childSection: String, parentSection: => String) =
if (childSection.indexOf("@inheritdoc") == -1)
childSection
- else
+ else
childSection.replaceAllLiterally("@inheritdoc", parentSection)
def getParentSection(section: (Int, Int)): String = {
@@ -275,9 +275,9 @@ trait DocComments { self: Global =>
}
child.substring(section._1, section._1 + 7) match {
- case param@("@param "|"@tparam"|"@throws") =>
+ case param@("@param "|"@tparam"|"@throws") =>
sectionString(extractSectionParam(child, section), parentNamedParams(param.trim))
- case _ =>
+ case _ =>
sectionString(extractSectionTag(child, section), parentTagMap)
}
}
@@ -367,7 +367,7 @@ trait DocComments { self: Global =>
case vname =>
lookupVariable(vname, site) match {
case Some(replacement) => replaceWith(replacement)
- case None => reporter.warning(sym.pos, "Variable " + vname + " undefined in comment for " + sym)
+ case None => reporter.warning(sym.pos, "Variable " + vname + " undefined in comment for " + sym + " in " + site)
}
}
}
@@ -58,7 +58,7 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor
case Right(sourceCode) =>
new compiler.Run() compileSources List(new BatchSourceFile("newSource", sourceCode))
}
-
+
if (reporter.hasErrors)
return None
@@ -80,6 +80,7 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor
val modelFactory = (
new { override val global: compiler.type = compiler }
with model.ModelFactory(compiler, settings)
+ with model.ModelFactoryImplicitSupport
with model.comment.CommentFactory
with model.TreeFactory {
override def templateShouldDocument(sym: compiler.Symbol) =
@@ -89,7 +90,8 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor
modelFactory.makeModel match {
case Some(madeModel) =>
- println("model contains " + modelFactory.templatesCount + " documentable templates")
+ if (settings.reportModel)
+ println("model contains " + modelFactory.templatesCount + " documentable templates")
Some(madeModel)
case None =>
println("no documentable class found in compilation units")
Oops, something went wrong.

0 comments on commit 355264f

Please sign in to comment.