diff options
author | Martin Odersky <odersky@gmail.com> | 2009-12-07 17:31:49 +0000 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2009-12-07 17:31:49 +0000 |
commit | 1e88594f358d08b5e9b22ba87280003a581359e4 (patch) | |
tree | e68b6d1095ff1b1345e86d8a67c1f874849a31ff /src/compiler | |
parent | de1d172a15e20ec7e96d96d5cea5cb41162b9f75 (diff) | |
download | scala-1e88594f358d08b5e9b22ba87280003a581359e4.tar.gz scala-1e88594f358d08b5e9b22ba87280003a581359e4.tar.bz2 scala-1e88594f358d08b5e9b22ba87280003a581359e4.zip |
new doc comment generation, including some new ...
new doc comment generation, including some new style doc comments in
collection classes.
Diffstat (limited to 'src/compiler')
16 files changed, 514 insertions, 109 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 6b2f3101a6..645eb1ecf6 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -115,31 +115,6 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable val treeBrowser = treeBrowsers.create() - -// val copy = new LazyTreeCopier() - - /** A map of all doc comments, indexed by symbols. - * Only active in onlyPresentation mode - */ - val comments = - if (onlyPresentation) new HashMap[Symbol,String] - else null - - /** A map of all doc comments source file offsets, - * indexed by symbols. - * Only active in onlyPresentation mode - */ - val commentOffsets = - if (onlyPresentation) new HashMap[Symbol,Int] - else null - - /** A map of argument names for methods - * !!! can be dropped once named method arguments are in !!! - */ - val methodArgumentNames = - if (onlyPresentation) new HashMap[Symbol,List[List[Symbol]]] - else null - // ------------ Hooks for interactive mode------------------------- /** Called every time an AST node is succesfully typedchecked in typerPhase. @@ -302,8 +277,8 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable val unit0 = currentRun.currentUnit try { currentRun.currentUnit = unit - reporter.setSource(unit.source) - if (!cancelled(unit)) apply(unit) + if (!cancelled(unit)) + reporter.withSource(unit.source) { apply(unit) } currentRun.advanceUnit } finally { //assert(currentRun.currentUnit == unit) @@ -889,11 +864,11 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable var localPhase = firstPhase.asInstanceOf[GlobalPhase] while (localPhase != null && (localPhase.id < globalPhase.id || localPhase.id <= namerPhase.id)/* && !reporter.hasErrors*/) { val oldSource = reporter.getSource - reporter.setSource(unit.source) - atPhase(localPhase)(localPhase.applyPhase(unit)) + reporter.withSource(unit.source) { + atPhase(localPhase)(localPhase.applyPhase(unit)) + } val newLocalPhase = localPhase.next.asInstanceOf[GlobalPhase] localPhase = if (localPhase == newLocalPhase) null else newLocalPhase - reporter.setSource(oldSource) } refreshProgress } diff --git a/src/compiler/scala/tools/nsc/ast/DocComments.scala b/src/compiler/scala/tools/nsc/ast/DocComments.scala new file mode 100755 index 0000000000..d754f12dee --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/DocComments.scala @@ -0,0 +1,365 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2009 LAMP/EPFL + * @author Martin Odersky + */ +// $Id: Unapplies.scala 19206 2009-10-21 20:57:27Z extempore $ + +package scala.tools.nsc +package ast + +import symtab._ +import util.{Position, NoPosition} +import scala.collection.mutable.{HashMap, ListBuffer, StringBuilder} + +/* + * @author Martin Odersky + * @version 1.0 + */ +trait DocComments { self: SymbolTable => + + val docComments = new HashMap[Symbol, DocComment] // !!! todo: inherit from comment? + + private val defs = new HashMap[Symbol, Map[String, String]] { + override def default(key: Symbol) = Map() + } + + def getDocComment(sym: Symbol): Option[DocComment] = { + docComments get sym match { + case None => + mapFind(sym.allOverriddenSymbols)(docComments.get) + case someSym => + someSym + } + } + + private def mapFind[A, B](xs: Iterable[A])(f: A => Option[B]): Option[B] = { + var res: Option[B] = None + val it = xs.iterator + while (res.isEmpty && it.hasNext) { + res = f(it.next()) + } + res + } + + /** The raw doc comment of symbol `sym`, as it appears in the source text, "" if missing. + */ + def rawDocComment(sym: Symbol): String = getDocComment(sym) map (_.raw) getOrElse "" + + /** The processed doc comment of symbol `sym`, where + * Wiki syntax is expanded and @usecase or @define parts are removed. + * No variables are expanded yet. "" if missing. + */ + def templateDocComment(sym: Symbol): String = getDocComment(sym) map (_.template) getOrElse "" + + /** The cooked doc comment of symbol `sym` after variable expansion, or "" if missing. + * @param sym The symbol for which doc comment is returned + * @param site The class for which doc comments are generated + */ + def cookedDocComment(sym: Symbol, site: Symbol): String = + getDocComment(sym) map (_.expanded(site)) getOrElse "" + + /** The cooked doc comment of symbol `sym` after variable expansion, or "" if missing. + * @param sym The symbol for which doc comment is returned (site is always the containing class) + */ + def cookedDocComment(sym: Symbol): String = cookedDocComment(sym, sym.owner) + + /** The position of the doc comment of symbol `sym`, or NoPosition if missing */ + def docCommentPos(sym: Symbol): Position = getDocComment(sym) map (_.pos) getOrElse NoPosition + + def useCases(sym: Symbol, site: Symbol): List[(Symbol, String)] = { + def getUseCases(dc: DocComment) = { + for (uc <- dc.useCases; defn <- uc.expandedDefs(site)) yield + (defn, uc.comment.expanded(site)) + } + getDocComment(sym) map getUseCases getOrElse List() + } + + def useCases(sym: Symbol): List[(Symbol, String)] = useCases(sym, sym.owner) + + private val usecasePrefix = "@usecase " + private val definePrefix = "@define " + + /** Returns index of string `str` after `in` skipping longest + * sequence of space and tab characters. + */ + private def skipWhitespace(str: String, in: Int): Int = { + var idx = in + do { + idx += 1 + } while (idx < str.length && (str charAt idx) == ' ' || (str charAt idx) == '\t') + idx + } + + /** Returns index of string `str` after `in` skipping longest + * sequence of space and tab characters, possibly also containing + * a single `*' character. + */ + private def skipLineLead(str: String, in: Int, end: Int): Int = { + val idx = skipWhitespace(str, in) + if (idx < end && (str charAt idx) == '*') skipWhitespace(str, idx) + else idx + } + + /** Extracts variable name from a string, stripping any pair of surrounding braces */ + private def variableName(str: String): String = + if (str.length >= 2 && (str charAt 0) == '{' && (str charAt (str.length - 1)) == '}') + str.substring(1, str.length - 1) + else + str + + private def isVarPart(ch: Char) = + '0' <= ch && ch <= '9' || 'A' <= ch && ch <= 'Z' || 'a' <= ch && ch <= 'z' + + /** Returns index following variable, or start index if no variable was recognized + */ + private def skipVariable(str: String, start: Int): Int = { + var idx = start + if (idx < str.length && (str charAt idx) == '{') { + do idx += 1 + while (idx < str.length && (str charAt idx) != '}') + if (idx < str.length) idx + 1 else start + } else { + while (idx < str.length && isVarPart(str charAt idx)) + idx += 1 + idx + } + } + + private val wikiReplacements = List( + ("""(\n\s*\*?)(\s*\n)""" .r, """$1 <p>$2"""), + ("""\{\{\{(.*(?:\n.*)*)\}\}\}""".r, """<pre>$1</pre>"""), + ("""`([^`]*)`""" .r, """<code>$1</code>"""), + ("""__([^_]*)__""" .r, """<u>$1</u>"""), + ("""''([^']*)''""" .r, """<i>$1</i>"""), + ("""'''([^']*)'''""" .r, """<b>$1</b>"""), + ("""\^([^^]*)\^""" .r, """<sup>$1</sup>"""), + (""",,([^,]*),,""" .r, """<sub>$1</sub>""")) + private def expandWiki(str: String): String = + (str /: wikiReplacements) { (str1, regexRepl) => regexRepl._1 replaceAllIn(str1, regexRepl._2) } + + /** Lookup definition of variable. + * + * @param vble The variable for which a definition is searched + * @param owner The current owner in which variable definitions are searched. + * @param site The class for which doc comments are generated + */ + def lookupVariable(vble: String, site: Symbol): Option[String] = + if (site == NoSymbol) + None + else + mapFind(site.info.baseClasses)(defs(_).get(vble)) match { + case None => lookupVariable(vble, site.owner) + case someStr => someStr + } + + private var expandCount = 0 + private final val expandLimit = 10 + + /** Expand variable occurrences in string `str', until a fix point is reached or + * a expandLimit is exceeded. + * + * @param str The string to be expanded + * @param site The class for which doc comments are generated + * @return Expanded string + */ + private def expandVariables(str: String, site: Symbol): String = + if (expandCount < expandLimit) { + try { + val out = new StringBuilder + var start = 0 + var idx = 0 + while (idx < str.length) { + if ((str charAt idx) == '$') { + val vstart = idx + idx = skipVariable(str, idx + 1) + val vname = variableName(str.substring(vstart + 1, idx)) + if (vname.length > 0) { + lookupVariable(vname, site) match { + case Some(replacement) => + out append str.substring(start, vstart) + out append replacement + start = idx + case None => + println("no replacement for "+vname) // !!! + } + } else { + idx += 1 + } + } else { + idx += 1 + } + } + if (out.length == 0) str + else { + out append str.substring(start) + expandVariables(out.toString, site) + } + } finally { + expandCount -= 1 + } + } else throw new ExpansionLimitExceeded(str) + + case class DocComment(raw: String, pos: Position = NoPosition) { + + lazy val (template, defines, useCases) = { + val parts = decompose(raw) + val (defines, usecases) = parts.tail partition (_._1 startsWith definePrefix) + val templ = expandWiki(parts.head._1) + (templ, + defines map (d => expandWiki(d._1)), + usecases map (decomposeUseCase(_, templ))) + } + + def expanded(site: Symbol): String = + expandVariables(template, site) +/* + expansions get site match { + case Some(str) => str + case None => val str = + expandVariables(template, sym, site) + expansions += (site -> str) + str + } + private var expansions: Map[Symbol, String] = Map() +*/ + + + /** Decomposes a comment string into + * an initial comment and a list of @define and @usecase clauses, each with starting index + */ + private def decompose(str: String): List[(String, Int)] = { + val out = new ListBuffer[(String, Int)] + var segstart = 0 + var idx = 3 // skip initial "/**" + val end = str.length - 2 // stop before final "*/" + var eolIdx = idx + while (idx < end) { + if ((str charAt idx) == '\n') { + eolIdx = idx + idx = skipLineLead(str, idx, end) + if ((str charAt idx) == '@' && + (str.startsWith(definePrefix, idx) || str.startsWith(usecasePrefix, idx))) { + var segment = str.substring(segstart, eolIdx) + if (segstart == 0) segment += "*/" + out += ((segment, segstart)) + segstart = idx + } + } else idx += 1 + } + if (segstart == 0) + List((str, 0)) + else { + out += ((str.substring(segstart, eolIdx), segstart)) + out.toList + } + } + + def subPos(start: Int, end: Int) = + if (pos == NoPosition) NoPosition + else { + val start1 = pos.start + start + val end1 = pos.end + end + pos withStart start1 withPoint start1 withEnd end1 + } + + def decomposeUseCase(stroff: (String, Int), mainComment: String): UseCase = { + val str = stroff._1 + val offset = stroff._2 + val start = usecasePrefix.length + var idx = start + while (idx < str.length && (str charAt idx) != '\n') idx += 1 + val code = str.substring(start, idx) + val codePos = subPos(offset + usecasePrefix.length, offset + idx) + var firstParBreak = mainComment indexOf "<p>" + if (firstParBreak == -1) firstParBreak = mainComment.length - 2 + val comment = mainComment.substring(0, firstParBreak)+"<p>"+ + str.substring(skipLineLead(str, idx, str.length))+"*/" + val commentPos = subPos(offset + idx, offset + str.length) + UseCase(DocComment(comment, commentPos), code, codePos) + } + + def defineVariables(sym: Symbol) { + for (str <- defines) { + val start = definePrefix.length + var idx = skipVariable(str, start) + val vble = variableName(str.substring(start, idx)) + if (idx < str.length && (str charAt idx) == ' ') idx += 1 + defs(sym) += vble -> str.substring(idx) + } + if (defs(sym).nonEmpty) println("vars of "+sym+" = "+defs(sym)) // !!! + } + } + + case class UseCase(comment: DocComment, body: String, pos: Position) { + var defined: List[Symbol] = List() + var aliases: List[Symbol] = List() + + def expandedDefs(site: Symbol): List[Symbol] = { + + def select(site: Type, name: Name, orElse: => Type): Type = { + val member = site.nonPrivateMember(name) + if (member.isTerm) SingleType(site, member) + else if (member.isType) site.memberType(member) + else orElse + } + + def getSite(name: Name): Type = { + if (name == nme.this_) site.thisType + else { + def findIn(sites: List[Symbol]): Type = sites match { + case List() => NoType + case site :: sites1 => select(site.thisType, name, findIn(sites1)) + } + val (classes, pkgs) = site.ownerChain.span(!_.isPackageClass) + findIn(classes ::: List(pkgs.head, definitions.RootClass)) + } + } + + def getType(str: String): Type = { + val parts = str.split("""\.""").toList + val partnames = (parts.init map newTermName) ::: List(newTypeName(parts.last)) + (getSite(partnames.head) /: partnames.tail)(select(_, _, NoType)) + } + + val aliasExpansions: List[Type] = + for (alias <- aliases) yield + lookupVariable(alias.name.toString.substring(1), site) match { + case Some(repl) => + val tpe = getType(repl) + if (tpe != NoType) tpe + else { + val alias1 = alias.cloneSymbol(definitions.RootClass) + alias1.name = repl.toTypeName + TypeRef(NoPrefix, alias1, List()) + } + case None => + TypeRef(NoPrefix, alias, List()) + } + + def subst(sym: Symbol, from: List[Symbol], to: List[Type]): Type = + if (from.isEmpty) sym.tpe + else if (from.head == sym) to.head + else subst(sym, from.tail, to.tail) + + val substAliases = new TypeMap { + def apply(tp: Type) = mapOver(tp) match { + case tp1 @ TypeRef(pre, sym, args) if (sym.name.length > 1 && sym.name(0) == '$') => + subst(sym, aliases, aliasExpansions) match { + case TypeRef(pre, sym1, _) => + TypeRef(pre, sym1, args) + case _ => + tp1 + } + case tp1 => + tp1 + } + } + + for (defn <- defined) yield + defn.cloneSymbol(site).setInfo( + substAliases(defn.info).asSeenFrom(site.thisType, defn.owner)) + } + } + + class ExpansionLimitExceeded(str: String) extends Exception +} diff --git a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala index d18ecbbd37..4ba5f17a40 100644 --- a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala @@ -215,7 +215,7 @@ abstract class TreePrinters { } case DocDef(comment, definition) => - print(comment); println; print(definition) + print(comment.raw); println; print(definition) case Template(parents, self, body) => val currentOwner1 = currentOwner diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index 6c6311f143..3af729d4d7 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -570,7 +570,7 @@ trait Trees { // It's used primarily as a marker to check that the import has been typechecked. /** Documented definition, eliminated by analyzer */ - case class DocDef(comment: String, definition: Tree) + case class DocDef(comment: DocComment, definition: Tree) extends Tree { override def symbol: Symbol = definition.symbol override def symbol_=(sym: Symbol) { definition.symbol = sym } @@ -1072,7 +1072,7 @@ trait Trees { def TypeDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[TypeDef], rhs: Tree): TypeDef def LabelDef(tree: Tree, name: Name, params: List[Ident], rhs: Tree): LabelDef def Import(tree: Tree, expr: Tree, selectors: List[ImportSelector]): Import - def DocDef(tree: Tree, comment: String, definition: Tree): DocDef + def DocDef(tree: Tree, comment: DocComment, definition: Tree): DocDef def Template(tree: Tree, parents: List[Tree], self: ValDef, body: List[Tree]): Template def Block(tree: Tree, stats: List[Tree], expr: Tree): Block def CaseDef(tree: Tree, pat: Tree, guard: Tree, body: Tree): CaseDef @@ -1127,7 +1127,7 @@ trait Trees { new LabelDef(name, params, rhs).copyAttrs(tree) def Import(tree: Tree, expr: Tree, selectors: List[ImportSelector]) = new Import(expr, selectors).copyAttrs(tree) - def DocDef(tree: Tree, comment: String, definition: Tree) = + def DocDef(tree: Tree, comment: DocComment, definition: Tree) = new DocDef(comment, definition).copyAttrs(tree) def Template(tree: Tree, parents: List[Tree], self: ValDef, body: List[Tree]) = new Template(parents, self, body).copyAttrs(tree) @@ -1244,7 +1244,7 @@ trait Trees { if (expr0 == expr) && (selectors0 == selectors) => t case _ => treeCopy.Import(tree, expr, selectors) } - def DocDef(tree: Tree, comment: String, definition: Tree) = tree match { + def DocDef(tree: Tree, comment: DocComment, definition: Tree) = tree match { case t @ DocDef(comment0, definition0) if (comment0 == comment) && (definition0 == definition) => t case _ => treeCopy.DocDef(tree, comment, definition) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index de71ba30ab..c4c26e54b3 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -420,13 +420,13 @@ self => */ def joinComment(trees: => List[Tree]): List[Tree] = { val doc = in.flushDoc - if ((doc ne null) && doc._1.length > 0) { + if ((doc ne null) && doc.raw.length > 0) { val ts = trees val main = ts.find(_.pos.isOpaqueRange) ts map { t => - val dd = DocDef(doc._1, t) - val pos = doc._2.withEnd(t.pos.endOrPoint) + val dd = DocDef(doc, t) + val pos = doc.pos.withEnd(t.pos.endOrPoint) dd setPos (if (t eq main) pos else pos.makeTransparent) } } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 62c47e8606..3de13ed877 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -103,11 +103,11 @@ trait Scanners { /** buffer for the documentation comment */ var docBuffer: StringBuilder = null - var docOffset: Position = null + var docPos: Position = null /** Return current docBuffer and set docBuffer to null */ - def flushDoc = { - val ret = if (docBuffer != null) (docBuffer.toString, docOffset) else null + def flushDoc: DocComment = { + val ret = if (docBuffer != null) DocComment(docBuffer.toString, docPos) else null docBuffer = null ret } @@ -1091,8 +1091,8 @@ trait Scanners { } override def foundDocComment(value: String, start: Int, end: Int) { - docOffset = new RangePosition(unit.source, start, start, end) - unit.comment(docOffset, value) + docPos = new RangePosition(unit.source, start, start, end) + unit.comment(docPos, value) } } diff --git a/src/compiler/scala/tools/nsc/doc/DocFactory.scala b/src/compiler/scala/tools/nsc/doc/DocFactory.scala index 240ba17d51..3c969c41f8 100644 --- a/src/compiler/scala/tools/nsc/doc/DocFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/DocFactory.scala @@ -39,7 +39,7 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor override def onlyPresentation = true lazy val addSourceless = { val sless = new SourcelessComments { val global = compiler } - comments ++= sless.comments + docComments ++= sless.comments } } diff --git a/src/compiler/scala/tools/nsc/doc/SourcelessComments.scala b/src/compiler/scala/tools/nsc/doc/SourcelessComments.scala index 50888ba27c..9216fa6f23 100644 --- a/src/compiler/scala/tools/nsc/doc/SourcelessComments.scala +++ b/src/compiler/scala/tools/nsc/doc/SourcelessComments.scala @@ -21,9 +21,9 @@ abstract class SourcelessComments { lazy val comments = { - val comment = mutable.HashMap.empty[Symbol, String] + val comment = mutable.HashMap.empty[Symbol, DocComment] - comment(NothingClass) = """ + comment(NothingClass) = new DocComment(""" /** <p> * Class <code>Nothing</code> is - together with class <a href="Null.html"> * <code>Null</code></a> - at the bottom of the @@ -44,9 +44,9 @@ abstract class SourcelessComments { * instance of <code><a href="List.html">List</a>[T]</code>, for * any element type <code>T</code>. * </p> */ - """ + """) - comment(NullClass) = """ + comment(NullClass) = new DocComment(""" /** <p> * Class <code>Null</code> is - together with class <a href="Nothing.html"> * <code>Nothing</code> - at the bottom of the @@ -61,12 +61,12 @@ abstract class SourcelessComments { * it is not possible to assign <code>null</code> to a variable of * type <a href="Int.html"><code>Int</code></a>. * </p> */ - """ + """) /*******************************************************************/ /* Documentation for Any */ - comment(AnyClass) = """ + comment(AnyClass) = new DocComment(""" /** <p> * Class <code>Any</code> is the root of the <a * href="http://scala-lang.org/" @@ -77,9 +77,9 @@ abstract class SourcelessComments { * <a href="AnyRef.html"><code>AnyRef</code></a> and * <a href="AnyVal.html"><code>AnyVal</code></a>. * </p> */ - """ + """) - comment(Any_equals) = """ + comment(Any_equals) = new DocComment(""" /** This method is used to compare the receiver object (<code>this</code>) * with the argument object (<code>arg0</code>) for equivalence. * @@ -113,34 +113,34 @@ abstract class SourcelessComments { * @param arg0 the object to compare against this object for equality. * @return <code>true</code> if the receiver object is equivalent to the argument; <code>false</code> otherwise. * </p> */ - """ + """) - comment(Any_==) = """ + comment(Any_==) = new DocComment(""" /** `o == arg0` is the same as `o.equals(arg0)`. * <p> * @param arg0 the object to compare against this object for equality. * @return `true` if the receiver object is equivalent to the argument; `false` otherwise. * </p> */ - """ + """) - comment(Any_!=) = """ + comment(Any_!=) = new DocComment(""" /** `o != arg0` is the same as `!(o == (arg0))`. * <p> * @param arg0 the object to compare against this object for dis-equality. * @return `false` if the receiver object is equivalent to the argument; `true` otherwise. * </p> */ - """ + """) - comment(Any_toString) = """ + comment(Any_toString) = new DocComment(""" /** Returns a string representation of the object. * <p> * The default representation is platform dependent. * * @return a string representation of the object. * </p>*/ - """ + """) - comment(Any_asInstanceOf) = """ + comment(Any_asInstanceOf) = new DocComment(""" /**This method is used to cast the receiver object to be of type <code>T0</code>. * * <p>Note that the success of a cast at runtime is modulo Scala's @@ -156,9 +156,9 @@ abstract class SourcelessComments { * instance of erasure of type <code>T0</code>. * @return the receiver object. * </p> */ - """ + """) - comment(Any_isInstanceOf) = """ + comment(Any_isInstanceOf) = new DocComment(""" /** This method is used to test whether the dynamic type of the receiver object is <code>T0</code>. * * <p>Note that the test result of the test is modulo Scala's erasure @@ -172,9 +172,9 @@ abstract class SourcelessComments { * * @return <code>true</code> if the receiver object is an * instance of erasure of type <code>T0</code>; <code>false</code> otherwise. */ - """ + """) - comment(Any_hashCode) = """ + comment(Any_hashCode) = new DocComment(""" /** Returns a hash code value for the object. * * <p> @@ -195,36 +195,36 @@ abstract class SourcelessComments { * <p> * @return the hash code value for the object. * </p> */ - """ + """) /*******************************************************************/ /* Documentation for AnyRef */ - comment(AnyRefClass) = """ + comment(AnyRefClass) = new DocComment(""" /** <p> * Class <code>AnyRef</code> is the root class of all * <em>reference types</em>. * </p> */ - """ + """) - comment(Object_==) = """ + comment(Object_==) = new DocComment(""" /** <code>o == arg0</code> is the same as <code>if (o eq null) arg0 eq null else o.equals(arg0)</code>. * <p> * @param arg0 the object to compare against this object for equality. * @return <code>true</code> if the receiver object is equivalent to the argument; <code>false</code> otherwise. * </p> */ - """ + """) - comment(Object_ne) = """ + comment(Object_ne) = new DocComment(""" /** <code>o.ne(arg0)</code> is the same as <code>!(o.eq(arg0))</code>. * <p> * @param arg0 the object to compare against this object for reference dis-equality. * @return <code>false</code> if the argument is not a reference to the receiver object; <code>true</code> otherwise. * </p> */ - """ + """) - comment(Object_finalize) = """ + comment(Object_finalize) = new DocComment(""" /** This method is called by the garbage collector on the receiver object when garbage * collection determines that there are no more references to the object. * <p> @@ -232,9 +232,9 @@ abstract class SourcelessComments { * invoked, as well as the interaction between <code>finalize</code> * and non-local returns and exceptions, are all platform dependent. * </p> */ - """ + """) - comment(Object_clone) = """ + comment(Object_clone) = new DocComment(""" /** This method creates and returns a copy of the receiver object. * * <p> @@ -242,9 +242,9 @@ abstract class SourcelessComments { * * @return a copy of the receiver object. * </p> */ - """ + """) - comment(Object_getClass) = """ + comment(Object_getClass) = new DocComment(""" /** Returns a representation that corresponds to the dynamic class of the receiver object. * * <p> @@ -252,17 +252,17 @@ abstract class SourcelessComments { * * @return a representation that corresponds to the dynamic class of the receiver object. * </p> */ - """ + """) - comment(Object_notify) = """ + comment(Object_notify) = new DocComment(""" /** Wakes up a single thread that is waiting on the receiver object's monitor. */ - """ + """) - comment(Object_notifyAll) = """ + comment(Object_notifyAll) = new DocComment(""" /** Wakes up all threads that are waiting on the receiver object's monitor. */ - """ + """) - comment(Object_eq) = """ + comment(Object_eq) = new DocComment(""" /** This method is used to test whether the argument (<code>arg0</code>) is a reference to the * receiver object (<code>this</code>). * @@ -304,11 +304,11 @@ abstract class SourcelessComments { * @param arg0 the object to compare against this object for reference equality. * @return <code>true</code> if the argument is a reference to the receiver object; <code>false</code> otherwise. * </p> */ - """ + """) /*******************************************************************/ - comment(AnyValClass) = """ + comment(AnyValClass) = new DocComment(""" /** <p> * Class <code>AnyVal</code> is the root class of all * <em>value types</em>. @@ -338,25 +338,25 @@ abstract class SourcelessComments { * <a href="Double.html"><code>Double</code></a> are called * <em>floating point types</em>. * </p> */ - """ + """) - comment(BooleanClass) = """ + comment(BooleanClass) = new DocComment(""" /** <p> * Class <code>Boolean</code> has only two values: <code>true</code> * and <code>false</code>. * </p> */ - """ + """) - comment(UnitClass) = """ + comment(UnitClass) = new DocComment(""" /** <p> * Class <code>Unit</code> has only one value: <code>()</code>. * </p> */ - """ + """) List(ByteClass, CharClass, DoubleClass, LongClass, FloatClass, IntClass, ShortClass) foreach { sym => val maxValue = "MAX_" + sym.name.toString().toUpperCase() val minValue = "MIN_" + sym.name.toString().toUpperCase() - comment(sym) = """ + comment(sym) = new DocComment(""" /** <p> * Class <code>""" + sym.name + """</code> belongs to the value * classes whose instances are not represented as objects by the @@ -370,7 +370,7 @@ abstract class SourcelessComments { * Values <code>""" + maxValue + """</code> and <code>""" + minValue + """</code> * are in defined in object <a href="Math$object.html">scala.Math</a>. * </p> */ - """ + """) } comment diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index b8a219fb4d..35d212071e 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -512,11 +512,8 @@ self => */ private def applyPhase(phase: Phase, unit: CompilationUnit) { val oldSource = reporter.getSource - try { - reporter.setSource(unit.source) + reporter.withSource(unit.source) { atPhase(phase) { phase.asInstanceOf[GlobalPhase] applyPhase unit } - } finally { - reporter setSource oldSource } } } diff --git a/src/compiler/scala/tools/nsc/reporters/Reporter.scala b/src/compiler/scala/tools/nsc/reporters/Reporter.scala index b00590d501..99292f2338 100644 --- a/src/compiler/scala/tools/nsc/reporters/Reporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/Reporter.scala @@ -41,6 +41,15 @@ abstract class Reporter { private var source: SourceFile = _ def setSource(source: SourceFile) { this.source = source } def getSource: SourceFile = source + def withSource[A](src: SourceFile)(op: => A) = { + val oldSource = source + try { + source = src + op + } finally { + source = oldSource + } + } def info(pos: Position, msg: String, force: Boolean) { info0(pos, msg, INFO, force) } def warning(pos: Position, msg: String ) { info0(pos, msg, WARNING, false) } diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala b/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala index da0d237567..2a70c55cd6 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala @@ -6,7 +6,7 @@ package scala.tools.nsc package symtab -import ast.Trees +import ast.{Trees, DocComments} import util._ @@ -23,6 +23,7 @@ abstract class SymbolTable extends Names with AnnotationCheckers with Trees with Positions + with DocComments { def settings: Settings def rootLoader: LazyType diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala index 8d3d0bbf2a..ee0576d654 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala @@ -538,7 +538,7 @@ abstract class UnPickler { case other => errorBadSignature("Document comment not a string (" + other + ")") } val definition = readTreeRef() - DocDef(comment, definition) + DocDef(DocComment(comment, NoPosition), definition) case TEMPLATEtree => setSym() diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 1e8b93bf5d..2132be4d50 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -758,9 +758,6 @@ trait Namers { self: Analyzer => tpt setPos meth.pos.focus } - if (onlyPresentation && methodArgumentNames != null) - methodArgumentNames(meth) = vparamss.map(_.map(_.symbol)); - def convertToDeBruijn(vparams: List[Symbol], level: Int): TypeMap = new TypeMap { def debruijnFor(param: Symbol) = DeBruijnIndex(level, vparams indexOf param) diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index 682a7db991..a18fbe70d1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -151,6 +151,17 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT decls.enter(s) } } + if (settings.verbose.value && onlyPresentation && !sym.isAnonymousClass) { + println("========== scaladoc of "+sym+" =============================") + for (member <- sym.info.members) { + println(member+":"+sym.thisType.memberInfo(member)+"\n"+ + cookedDocComment(member, sym)) + for ((useCase, comment) <- useCases(member, sym)) { + println("usecase "+useCase+":"+useCase.info) + println(comment) + } + } + } super.transform(tree) case ModuleDef(_, _, _) => checkCompanionNameClashes(sym) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 7e4d6b7d7d..a610f85104 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -15,7 +15,7 @@ import scala.collection.mutable.{HashMap, ListBuffer} import scala.util.control.ControlException import scala.compat.Platform.currentTime import scala.tools.nsc.interactive.RangePositions -import scala.tools.nsc.util.{ Position, Set, NoPosition, SourceFile } +import scala.tools.nsc.util.{ Position, Set, NoPosition, SourceFile, BatchSourceFile } import symtab.Flags._ // Suggestion check whether we can do without priming scopes with symbols of outer scopes, @@ -689,8 +689,8 @@ trait Typers { self: Analyzer => else qual.tpe.nonLocalMember(name) } - def silent(op: Typer => Tree): AnyRef /* in fact, TypeError or Tree */ = { - val start = System.nanoTime() + def silent[T](op: Typer => T): Any /* in fact, TypeError or T */ = { +// val start = System.nanoTime() try { if (context.reportGeneralErrors) { val context1 = context.makeSilent(context.reportAmbiguousErrors) @@ -709,7 +709,7 @@ trait Typers { self: Analyzer => } catch { case ex: CyclicReference => throw ex case ex: TypeError => - failedSilent += System.nanoTime() - start +// failedSilent += System.nanoTime() - start ex }} @@ -1709,6 +1709,45 @@ trait Typers { self: Analyzer => } } + def typedUseCase(useCase: UseCase) { + def stringParser(str: String): syntaxAnalyzer.Parser = { + val file = new BatchSourceFile(context.unit.source.file, str) { + override def positionInUltimateSource(pos: Position) = { + pos.withSource(context.unit.source, useCase.pos.start) + } + } + val unit = new CompilationUnit(file) + new syntaxAnalyzer.UnitParser(unit) + } + val trees = stringParser(useCase.body+";").nonLocalDefOrDcl + val enclClass = context.enclClass.owner + def defineAlias(name: Name) = + if (context.scope.lookup(name) == NoSymbol) { + lookupVariable(name.toString.substring(1), enclClass) match { + case Some(repl) => + silent(_.typedTypeConstructor(stringParser(repl).typ())) match { + case tpt: Tree => + val alias = enclClass.newAliasType(useCase.pos, name) + val tparams = cloneSymbols(tpt.tpe.typeSymbol.typeParams, alias) + alias setInfo polyType(tparams, appliedType(tpt.tpe, tparams map (_.tpe))) + context.scope.enter(alias) + case _ => + } + case _ => + } + } + for (tree <- trees; t <- tree) + t match { + case Ident(name) if (name.length > 0 && name(0) == '$') => defineAlias(name) + case _ => + } + useCase.aliases = context.scope.toList + namer.enterSyms(trees) + typedStats(trees, NoSymbol) + useCase.defined = context.scope.toList -- useCase.aliases +// println("defined use cases: "+(useCase.defined map (sym => sym+":"+sym.tpe))) + } + /** * @param ddef ... * @return ... @@ -3701,12 +3740,18 @@ trait Typers { self: Analyzer => labelTyper(ldef).typedLabelDef(ldef) case ddef @ DocDef(comment, defn) => - val ret = typed(defn, mode, pt) - if ((comments ne null) && (defn.symbol ne null) && (defn.symbol ne NoSymbol)) { - comments(defn.symbol) = comment - commentOffsets(defn.symbol) = ddef.pos.startOrPoint + if (onlyPresentation && (sym ne null) && (sym ne NoSymbol)) { + docComments(sym) = comment + comment.defineVariables(sym) + val typer1 = newTyper(context.makeNewScope(tree, context.owner)) + for (useCase <- comment.useCases) + typer1.silent(_.typedUseCase(useCase)) match { + case ex: TypeError => + unit.warning(useCase.pos, ex.msg) + case _ => + } } - ret + typed(defn, mode, pt) case Annotated(constr, arg) => typedAnnotated(constr, typed(arg, mode, pt)) diff --git a/src/compiler/scala/tools/nsc/util/Position.scala b/src/compiler/scala/tools/nsc/util/Position.scala index 999dca3927..806c885291 100644 --- a/src/compiler/scala/tools/nsc/util/Position.scala +++ b/src/compiler/scala/tools/nsc/util/Position.scala @@ -109,6 +109,9 @@ trait Position { /** The same position with a different point value (if a range or offset) */ def withPoint(off: Int) = this + /** The same position with a different source value, and its values shifted by given offset */ + def withSource(source: SourceFile, shift: Int) = this + /** If this is a range, the union with the other range, with the point of this position. * Otherwise, this position */ @@ -200,6 +203,7 @@ class OffsetPosition(override val source: SourceFile, override val point: Int) e override def isDefined = true override def pointOrElse(default: Int): Int = point override def withPoint(off: Int) = new OffsetPosition(source, off) + override def withSource(source: SourceFile, shift: Int) = new OffsetPosition(source, point + shift) override def line: Int = source.offsetToLine(point) + 1 @@ -240,6 +244,7 @@ extends OffsetPosition(source, point) { override def withStart(off: Int) = new RangePosition(source, off, point, end) override def withEnd(off: Int) = new RangePosition(source, start, point, off) override def withPoint(off: Int) = new RangePosition(source, start, off, end) + override def withSource(source: SourceFile, shift: Int) = new RangePosition(source, start + shift, point + shift, end + shift) override def focusStart = new OffsetPosition(source, start) override def focus = { if (focusCache eq NoPosition) focusCache = new OffsetPosition(source, point) |