diff options
Diffstat (limited to 'src/compiler')
18 files changed, 304 insertions, 103 deletions
diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index febc2ef330..9678d2b8cd 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -500,8 +500,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => final def isClassLocalToConstructor = isClass && hasFlag(INCONSTRUCTOR) final def isDerivedValueClass = - isClass && info.parents.headOption.getOrElse(AnyClass.tpe).typeSymbol == AnyValClass && - !isPrimitiveValueClass + isClass && info.firstParent.typeSymbol == AnyValClass && !isPrimitiveValueClass final def isMethodWithExtension = isMethod && owner.isDerivedValueClass && !isParamAccessor && !isConstructor && !hasFlag(SUPERACCESSOR) diff --git a/src/compiler/scala/reflect/internal/Trees.scala b/src/compiler/scala/reflect/internal/Trees.scala index 77ea7392a8..9b1712b790 100644 --- a/src/compiler/scala/reflect/internal/Trees.scala +++ b/src/compiler/scala/reflect/internal/Trees.scala @@ -283,10 +283,16 @@ trait Trees extends api.Trees { self: SymbolTable => } } - private object posAssigner extends Traverser { + trait PosAssigner extends Traverser { + var pos: Position + } + protected[this] lazy val posAssigner: PosAssigner = new DefaultPosAssigner + + protected class DefaultPosAssigner extends PosAssigner { var pos: Position = _ override def traverse(t: Tree) { - if (t != EmptyTree && t.pos == NoPosition) { + if (t eq EmptyTree) () + else if (t.pos == NoPosition) { t.setPos(pos) super.traverse(t) // TODO: bug? shouldn't the traverse be outside of the if? // @PP: it's pruning whenever it encounters a node with a diff --git a/src/compiler/scala/tools/nsc/ast/DocComments.scala b/src/compiler/scala/tools/nsc/ast/DocComments.scala index 6a6379cca2..678f7b3028 100755 --- a/src/compiler/scala/tools/nsc/ast/DocComments.scala +++ b/src/compiler/scala/tools/nsc/ast/DocComments.scala @@ -19,6 +19,8 @@ import scala.collection.mutable */ trait DocComments { self: Global => + var cookedDocComments = Map[Symbol, String]() + def reporter: Reporter /** The raw doc comment map */ @@ -50,21 +52,29 @@ trait DocComments { self: Global => else sym.owner.ancestors map (sym overriddenSymbol _) filter (_ != NoSymbol) } - /** The raw doc comment of symbol `sym`, minus @usecase and @define sections, augmented by + /** The raw doc comment of symbol `sym`, minus usecase and define sections, augmented by * missing sections of an inherited doc comment. * If a symbol does not have a doc comment but some overridden version of it does, * the doc comment of the overridden version is copied instead. */ - def cookedDocComment(sym: Symbol, docStr: String = ""): String = { - val ownComment = if (docStr.length == 0) docComments get sym map (_.template) getOrElse "" - else DocComment(docStr).template - superComment(sym) match { - case None => - ownComment - case Some(sc) => - if (ownComment == "") sc - else merge(sc, ownComment, sym) - } + def cookedDocComment(sym: Symbol, docStr: String = ""): String = cookedDocComments.get(sym) match { + case Some(comment) => + comment + case None => + val ownComment = if (docStr.length == 0) docComments get sym map (_.template) getOrElse "" + else DocComment(docStr).template + val comment = superComment(sym) match { + case None => + if (ownComment.indexOf("@inheritdoc") != -1) + reporter.warning(sym.pos, "The comment for " + sym + + " contains @inheritdoc, but no parent comment is available to inherit from.") + ownComment.replaceAllLiterally("@inheritdoc", "<invalid inheritdoc annotation>") + case Some(sc) => + if (ownComment == "") sc + else expandInheritdoc(sc, merge(sc, ownComment, sym), sym) + } + cookedDocComments += (sym -> comment) + comment } /** The cooked doc comment of symbol `sym` after variable expansion, or "" if missing. @@ -99,10 +109,18 @@ trait DocComments { self: Global => */ def useCases(sym: Symbol, site: Symbol): List[(Symbol, String, Position)] = { def getUseCases(dc: DocComment) = { - for (uc <- dc.useCases; defn <- uc.expandedDefs(sym, site)) yield - (defn, - expandVariables(merge(cookedDocComment(sym), uc.comment.raw, defn), sym, site), - uc.pos) + val fullSigComment = cookedDocComment(sym) + for (uc <- dc.useCases; defn <- uc.expandedDefs(sym, site)) yield { + // use cases comments go through a series of transformations: + // 1 - filling in missing sections from the full signature + // 2 - expanding explicit inheritance @inheritdoc tags + // 3 - expanding variables like $COLL + val useCaseCommentRaw = uc.comment.raw + val useCaseCommentMerged = merge(fullSigComment, useCaseCommentRaw, defn) + val useCaseCommentInheritdoc = expandInheritdoc(fullSigComment, useCaseCommentMerged, sym) + val useCaseCommentVariables = expandVariables(useCaseCommentInheritdoc, sym, site) + (defn, useCaseCommentVariables, uc.pos) + } } getDocComment(sym) map getUseCases getOrElse List() } @@ -201,6 +219,80 @@ trait DocComments { self: Global => } } + /** + * Expand inheritdoc tags + * - for the main comment we transform the inheritdoc into the super variable, + * and the variable expansion can expand it further + * - for the param, tparam and throws sections we must replace comments on the spot + * + * This is done separately, for two reasons: + * 1. It takes longer to run compared to merge + * 2. The inheritdoc annotation should not be used very often, as building the comment from pieces severely + * impacts performance + */ + def expandInheritdoc(src: String, dst: String, sym: Symbol): String = + if (dst.indexOf("@inheritdoc") == -1) + dst + else { + val srcSections = tagIndex(src) + val dstSections = tagIndex(dst) + val srcTagMap = sectionTagMap(src, srcSections) + val srcNamedParams = Map() + + ("@param" -> paramDocs(src, "@param", srcSections)) + + ("@tparam" -> paramDocs(src, "@tparam", srcSections)) + + ("@throws" -> paramDocs(src, "@throws", srcSections)) + + val out = new StringBuilder + + def replaceInheritdoc(src: String, dst: String) = + if (dst.indexOf("@inheritdoc") == -1) + dst + else + dst.replaceAllLiterally("@inheritdoc", src) + + def getSourceSection(section: (Int, Int)): String = { + + def getSectionHeader = extractSectionTag(dst, section) match { + case param@("@param"|"@tparam"|"@throws") => param + " " + extractSectionParam(dst, section) + case other => other + } + + def sectionString(param: String, paramMap: Map[String, (Int, Int)]): String = + paramMap.get(param) match { + case Some(section) => + // Cleanup the section tag and parameter + val sectionTextBounds = extractSectionText(src, section) + cleanupSectionText(src.substring(sectionTextBounds._1, sectionTextBounds._2)) + case None => + reporter.info(sym.pos, "The \"" + getSectionHeader + "\" annotation of the " + sym + + " comment contains @inheritdoc, but the corresponding section in the parent is not defined.", true) + "<invalid inheritdoc annotation>" + } + + dst.substring(section._1, section._1 + 7) match { + case param@("@param "|"@tparam"|"@throws") => sectionString(extractSectionParam(dst, section), srcNamedParams(param.trim)) + case _ => sectionString(extractSectionTag(dst, section), srcTagMap) + } + } + + def mainComment(str: String, sections: List[(Int, Int)]): String = + if (str.trim.length > 3) + str.trim.substring(3, startTag(str, sections)) + else + "" + + // Append main comment + out.append("/**") + out.append(replaceInheritdoc(mainComment(src, srcSections), mainComment(dst, dstSections))) + + // Append sections + for (section <- dstSections) + out.append(replaceInheritdoc(getSourceSection(section), dst.substring(section._1, section._2))) + + out.append("*/") + out.toString + } + /** Maps symbols to the variable -> replacement maps that are defined * in their doc comments */ diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index a1d3846557..43c231cf2d 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -16,6 +16,47 @@ import scala.reflect.internal.Flags.TRAIT trait Trees extends reflect.internal.Trees { self: Global => + def treeLine(t: Tree): String = + if (t.pos.isDefined && t.pos.isRange) t.pos.lineContent.drop(t.pos.column - 1).take(t.pos.end - t.pos.start + 1) + else t.summaryString + + def treeStatus(t: Tree, enclosingTree: Tree = null) = { + val parent = if (enclosingTree eq null) " " else " P#%5s".format(enclosingTree.id) + + "[L%4s%8s] #%-6s %-15s %-10s // %s".format(t.pos.safeLine, parent, t.id, t.pos.show, t.shortClass, treeLine(t)) + } + def treeSymStatus(t: Tree) = { + val line = if (t.pos.isDefined) "line %-4s".format(t.pos.safeLine) else " " + "#%-5s %s %-10s // %s".format(t.id, line, t.shortClass, + if (t.symbol ne NoSymbol) "(" + t.symbol.fullLocationString + ")" + else treeLine(t) + ) + } + + class ValidatingPosAssigner extends PosAssigner { + var pos: Position = _ + override def traverse(t: Tree) { + if (t eq EmptyTree) () + else if (t.pos == NoPosition) super.traverse(t setPos pos) + else if (globalPhase.id <= currentRun.picklerPhase.id) { + // When we prune due to encountering a position, traverse the + // pruned children so we can warn about those lacking positions. + t.children foreach { c => + if ((c eq EmptyTree) || (c eq emptyValDef)) () + else if (c.pos == NoPosition) { + reporter.warning(t.pos, " Positioned tree has unpositioned child in phase " + globalPhase) + inform("parent: " + treeSymStatus(t)) + inform(" child: " + treeSymStatus(c) + "\n") + } + } + } + } + } + + override protected[this] lazy val posAssigner: PosAssigner = + if (settings.Yrangepos.value && settings.debug.value || settings.Yposdebug.value) new ValidatingPosAssigner + else new DefaultPosAssigner + // --- additional cases -------------------------------------------------------- /** Only used during parsing */ case class Parens(args: List[Tree]) extends Tree diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index ab6125df61..ccebcfa54d 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -394,7 +394,7 @@ self => // object Main def moduleName = newTermName(ScriptRunner scriptMain settings) - def moduleBody = Template(List(scalaAnyRefConstr), emptyValDef, List(emptyInit, mainDef)) + def moduleBody = Template(List(atPos(o2p(in.offset))(scalaAnyRefConstr)), emptyValDef, List(emptyInit, mainDef)) def moduleDef = ModuleDef(NoMods, moduleName, moduleBody) // package <empty> { ... } @@ -2738,7 +2738,7 @@ self => def anyrefParents() = { val caseParents = if (mods.isCase) List(productConstr, serializableConstr) else Nil parents0 ::: caseParents match { - case Nil => List(scalaAnyRefConstr) + case Nil => List(atPos(o2p(in.offset))(scalaAnyRefConstr)) case ps => ps } } 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 5e5320ca9a..e35286b281 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala @@ -438,7 +438,9 @@ class Template(tpl: DocTemplateEntity) extends HtmlPage { if(!comment.throws.isEmpty) { <dt>Exceptions thrown</dt> <dd>{ - val exceptionsXml: Iterable[scala.xml.NodeSeq] = (for(exception <- comment.throws ) yield <span class="cmt">{Text(exception._1) ++ bodyToHtml(exception._2)}</span> ) + val exceptionsXml: Iterable[scala.xml.NodeSeq] = + for(exception <- comment.throws.toList.sortBy(_._1) ) yield + <span class="cmt">{Text(exception._1) ++ bodyToHtml(exception._2)}</span> exceptionsXml.reduceLeft(_ ++ Text("") ++ _) }</dd> } else NodeSeq.Empty 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 efa524503c..b088c643cb 100644 --- a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala @@ -38,7 +38,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => val key = (sym, inTpl) if (commentCache isDefinedAt key) Some(commentCache(key)) - else { // not reached for use-case comments + else { val c = defineComment(sym, inTpl) if (c isDefined) commentCache += (sym, inTpl) -> c.get c diff --git a/src/compiler/scala/tools/nsc/interactive/RangePositions.scala b/src/compiler/scala/tools/nsc/interactive/RangePositions.scala index d1f738a435..d08a363a9d 100644 --- a/src/compiler/scala/tools/nsc/interactive/RangePositions.scala +++ b/src/compiler/scala/tools/nsc/interactive/RangePositions.scala @@ -168,7 +168,7 @@ self: scala.tools.nsc.Global => /** Position a tree. * This means: Set position of a node and position all its unpositioned children. */ - override def atPos[T <: Tree](pos: Position)(tree: T): T = + override def atPos[T <: Tree](pos: Position)(tree: T): T = { if (pos.isOpaqueRange) { if (!tree.isEmpty && tree.pos == NoPosition) { tree.setPos(pos) @@ -182,6 +182,7 @@ self: scala.tools.nsc.Global => } else { super.atPos(pos)(tree) } + } // ---------------- Validating positions ---------------------------------- @@ -190,26 +191,33 @@ self: scala.tools.nsc.Global => val source = if (tree.pos.isDefined) tree.pos.source else "" inform("== "+prefix+" tree ["+tree.id+"] of type "+tree.productPrefix+" at "+tree.pos.show+source) inform("") - inform(tree.toString) + inform(treeStatus(tree)) inform("") } def positionError(msg: String)(body : => Unit) { - inform("======= Bad positions: "+msg) - inform("") + inform("======= Position error\n" + msg) body - inform("=== While validating") - inform("") - inform(tree.toString) - inform("") + inform("\nWhile validating #" + tree.id) + inform(treeStatus(tree)) + inform("\nChildren:") + tree.children map (t => " " + treeStatus(t, tree)) foreach inform inform("=======") throw new ValidateException(msg) } def validate(tree: Tree, encltree: Tree): Unit = { + if (!tree.isEmpty) { + if (settings.Yposdebug.value && (settings.verbose.value || settings.Yrangepos.value)) + println("[%10s] %s".format("validate", treeStatus(tree, encltree))) + if (!tree.pos.isDefined) - positionError("Unpositioned tree ["+tree.id+"]") { reportTree("Unpositioned", tree) } + positionError("Unpositioned tree #"+tree.id) { + inform("%15s %s".format("unpositioned", treeStatus(tree, encltree))) + inform("%15s %s".format("enclosing", treeStatus(encltree))) + encltree.children foreach (t => inform("%15s %s".format("sibling", treeStatus(t, encltree)))) + } if (tree.pos.isRange) { if (!encltree.pos.isRange) positionError("Synthetic tree ["+encltree.id+"] contains nonsynthetic tree ["+tree.id+"]") { diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index e949cb3eb2..5f3c7ec32c 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -162,6 +162,7 @@ trait ScalaSettings extends AbsScalaSettings val Ybuildmanagerdebug = BooleanSetting ("-Ybuild-manager-debug", "Generate debug information for the Refined Build Manager compiler.") val Ytyperdebug = BooleanSetting ("-Ytyper-debug", "Trace all type assignments.") + val Yposdebug = BooleanSetting ("-Ypos-debug", "Trace position validation.") val Yinferdebug = BooleanSetting ("-Yinfer-debug", "Trace type inference and implicit search.") val Ypmatdebug = BooleanSetting ("-Ypmat-debug", "Trace all pattern matcher activity.") val Yreifycopypaste = diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala index 555d0700ae..39e2cbe694 100644 --- a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala +++ b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala @@ -264,11 +264,10 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure => else DefDef(clazz.primaryConstructor, Block(List(), Literal(Constant()))) :: stats private def implTemplate(clazz: Symbol, templ: Template): Template = atPos(templ.pos) { - val templ1 = atPos(templ.pos) { - Template(templ.parents, emptyValDef, - addMixinConstructorDef(clazz, templ.body map implMemberDef)) - .setSymbol(clazz.newLocalDummy(templ.pos)) - } + val templ1 = ( + Template(templ.parents, emptyValDef, addMixinConstructorDef(clazz, templ.body map implMemberDef)) + setSymbol clazz.newLocalDummy(templ.pos) + ) templ1.changeOwner(templ.symbol.owner -> clazz, templ.symbol -> templ1.symbol) templ1 } diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala index e6ad7cb922..4c3972519a 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -105,7 +105,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { } else if (currentOwner.isStaticOwner) { super.transform(tree) } else tree - case DefDef(mods, name, tparams, vparamss, tpt, rhs) if tree.symbol.isMethodWithExtension => + case DefDef(_, _, tparams, vparamss, _, rhs) if tree.symbol.isMethodWithExtension => val companion = currentOwner.companionModule val origMeth = tree.symbol val extensionName = extensionNames(origMeth).head @@ -132,15 +132,13 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { gen.mkTypeApply(gen.mkAttributedRef(companion), extensionMeth, origTpeParams map (_.tpe)), List(This(currentOwner))) val extensionCall = atOwner(origMeth) { - localTyper.typed { - atPos(rhs.pos) { - (extensionCallPrefix /: vparamss) { - case (fn, params) => Apply(fn, params map (param => Ident(param.symbol))) - } + localTyper.typedPos(rhs.pos) { + (extensionCallPrefix /: vparamss) { + case (fn, params) => Apply(fn, params map (param => Ident(param.symbol))) } } } - treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, extensionCall) + deriveDefDef(tree)(_ => extensionCall) case _ => super.transform(tree) } @@ -148,14 +146,12 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = super.transformStats(stats, exprOwner) map { - case stat @ ModuleDef(mods, name, tmpl @ Template(parents, self, body)) => - extensionDefs.remove(stat.symbol) match { - case Some(buf) => - val extensionDefs = buf.toList map { mdef => atOwner(stat.symbol) { localTyper.typed(mdef) } } - treeCopy.ModuleDef(stat, mods, name, treeCopy.Template(tmpl, parents, self, body ++ extensionDefs)) - case None => - stat - } + case md @ ModuleDef(_, _, _) if extensionDefs contains md.symbol => + val defns = extensionDefs(md.symbol).toList map (member => + atOwner(md.symbol)(localTyper.typedPos(md.pos.focus)(member)) + ) + extensionDefs -= md.symbol + deriveModuleDef(md)(tmpl => deriveTemplate(tmpl)(_ ++ defns)) case stat => stat } diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index d1e95e3a27..dfadd8d60e 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -86,6 +86,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * nor do they have a setter (not if they are vals anyway). The usual * logic for setting bitmaps does therefor not work for such fields. * That's why they are excluded. + * Note: The `checkinit` option does not check if transient fields are initialized. */ private def needsInitFlag(sym: Symbol) = ( settings.checkInit.value @@ -95,6 +96,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { && !sym.accessed.hasFlag(PRESUPER) && !sym.isOuterAccessor && !(sym.owner isSubClass DelayedInitClass) + && !(sym.isGetter && (sym.accessed hasAnnotation TransientAttr)) ) /** Maps all parts of this type that refer to implementation classes to diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index 088a56cd7b..f32ad9293c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -69,6 +69,9 @@ trait MethodSynthesis { import synthesisUtil._ class ClassMethodSynthesis(val clazz: Symbol, localTyper: Typer) { + def mkThis = This(clazz) setPos clazz.pos.focus + def mkThisSelect(sym: Symbol) = atPos(clazz.pos.focus)(Select(mkThis, sym)) + private def isOverride(name: TermName) = clazzMember(name).alternatives exists (sym => !sym.isDeferred && (sym.owner != clazz)) diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 955d51bf8d..c5fb13a5a9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -456,7 +456,7 @@ trait Namers extends MethodSynthesis { // The object Foo is still in scope, but because it is not compiled in current run // it should be ditched and a new one created. if (m != NoSymbol && currentRun.compiles(m)) m - else enterSyntheticSym(creator(cdef)) + else enterSyntheticSym(atPos(cdef.pos.focus)(creator(cdef))) } private def checkSelectors(tree: Import): Unit = { @@ -1270,11 +1270,12 @@ trait Namers extends MethodSynthesis { if (sym.isModule) annotate(sym.moduleClass) def getSig = tree match { - case cdef @ ClassDef(_, _, tparams, impl) => + case cdef @ ClassDef(_, name, tparams, impl) => val clazz = tree.symbol val result = createNamer(tree).classSig(tparams, impl) clazz setInfo result if (clazz.isDerivedValueClass) { + log("Ensuring companion for derived value class " + name + " at " + cdef.pos.show) clazz setFlag FINAL enclosingNamerWithScope(clazz.owner.info.decls).ensureCompanionObject(cdef) } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 1e17cb2e3f..ec42d251ff 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1523,16 +1523,11 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R case _ => () } - // verify classes extending AnyVal meet the requirements - // (whatever those are to be, but at least: @inline annotation) + // Verify classes extending AnyVal meet the requirements private def checkAnyValSubclass(clazz: Symbol) = { - if ((clazz isSubClass AnyValClass) && (clazz ne AnyValClass) && !isPrimitiveValueClass(clazz)) { + if ((clazz isSubClass AnyValClass) && !isPrimitiveValueClass(clazz)) { if (clazz.isTrait) unit.error(clazz.pos, "Only classes (not traits) are allowed to extend AnyVal") - /* [Martin] That one is already taken care of by Typers - if (clazz.tpe <:< AnyRefClass.tpe) - unit.error(clazz.pos, "Classes which extend AnyVal may not have an ancestor which inherits AnyRef") - */ } } diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index f9d41bcc5e..2f4eff30d2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -51,11 +51,9 @@ trait SyntheticMethods extends ast.TreeDSL { if (clazz0 == AnyValClass || isPrimitiveValueClass(clazz0)) return { if (clazz0.info member nme.getClass_ isDeferred) { - val getClassMethod = createMethod(nme.getClass_, getClassReturnType(clazz.tpe)) { sym => - // XXX dummy implementation for now - NULL - } - treeCopy.Template(templ, templ.parents, templ.self, templ.body :+ getClassMethod) + // XXX dummy implementation for now + val getClassMethod = createMethod(nme.getClass_, getClassReturnType(clazz.tpe))(_ => NULL) + deriveTemplate(templ)(_ :+ getClassMethod) } else templ } @@ -89,7 +87,7 @@ trait SyntheticMethods extends ast.TreeDSL { ) def forwardToRuntime(method: Symbol): Tree = - forwardMethod(method, getMember(ScalaRunTimeModule, method.name prepend "_"))(This(clazz) :: _) + forwardMethod(method, getMember(ScalaRunTimeModule, method.name prepend "_"))(mkThis :: _) // Any member, including private def hasConcreteImpl(name: Name) = @@ -109,7 +107,7 @@ trait SyntheticMethods extends ast.TreeDSL { } def productIteratorMethod = { createMethod(nme.productIterator, iteratorOfType(accessorLub))(_ => - gen.mkMethodCall(ScalaRunTimeModule, nme.typedProductIterator, List(accessorLub), List(This(clazz))) + gen.mkMethodCall(ScalaRunTimeModule, nme.typedProductIterator, List(accessorLub), List(mkThis)) ) } def projectionMethod(accessor: Symbol, num: Int) = { @@ -157,8 +155,8 @@ trait SyntheticMethods extends ast.TreeDSL { def equalsCore(eqmeth: Symbol, accessors: List[Symbol]) = { val otherName = context.unit.freshTermName(clazz.name + "$") val otherSym = eqmeth.newValue(otherName, eqmeth.pos, SYNTHETIC) setInfo clazz.tpe - val pairwise = accessors map (acc => fn(Select(This(clazz), acc), acc.tpe member nme.EQ, Select(Ident(otherSym), acc))) - val canEq = gen.mkMethodCall(otherSym, nme.canEqual_, Nil, List(This(clazz))) + val pairwise = accessors map (acc => fn(Select(mkThis, acc), acc.tpe member nme.EQ, Select(Ident(otherSym), acc))) + val canEq = gen.mkMethodCall(otherSym, nme.canEqual_, Nil, List(mkThis)) val tests = if (clazz.isDerivedValueClass || clazz.isFinal && syntheticCanEqual) pairwise else pairwise :+ canEq thatTest(eqmeth) AND Block( @@ -181,9 +179,9 @@ trait SyntheticMethods extends ast.TreeDSL { def equalsCaseClassMethod: Tree = createMethod(nme.equals_, List(AnyClass.tpe), BooleanClass.tpe) { m => if (accessors.isEmpty) if (clazz.isFinal) thatTest(m) - else thatTest(m) AND ((thatCast(m) DOT nme.canEqual_)(This(clazz))) + else thatTest(m) AND ((thatCast(m) DOT nme.canEqual_)(mkThis)) else - (This(clazz) ANY_EQ Ident(m.firstParam)) OR equalsCore(m, accessors) + (mkThis ANY_EQ Ident(m.firstParam)) OR equalsCore(m, accessors) } /** The equality method for value classes @@ -200,9 +198,7 @@ trait SyntheticMethods extends ast.TreeDSL { * def hashCode(): Int = this.underlying.hashCode */ def hashCodeDerivedValueClassMethod: Tree = createMethod(nme.hashCode_, Nil, IntClass.tpe) { m => - Select( - Select(This(clazz), clazz.firstParamAccessor), - nme.hashCode_) + Select(mkThisSelect(clazz.firstParamAccessor), nme.hashCode_) } /** The _1, _2, etc. methods to implement ProductN. @@ -217,7 +213,7 @@ trait SyntheticMethods extends ast.TreeDSL { List( Product_productPrefix -> (() => constantNullary(nme.productPrefix, clazz.name.decode)), Product_productArity -> (() => constantNullary(nme.productArity, arity)), - Product_productElement -> (() => perElementMethod(nme.productElement, accessorLub)(Select(This(clazz), _))), + Product_productElement -> (() => perElementMethod(nme.productElement, accessorLub)(mkThisSelect)), Product_iterator -> (() => productIteratorMethod), Product_canEqual -> (() => canEqualMethod) // This is disabled pending a reimplementation which doesn't add any diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 9a2ef88821..25f3e7af5c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1295,7 +1295,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } def parentTypes(templ: Template): List[Tree] = - if (templ.parents.isEmpty) List(TypeTree(AnyRefClass.tpe)) + if (templ.parents.isEmpty) List(atPos(templ.pos)(TypeTree(AnyRefClass.tpe))) else try { val clazz = context.owner // Normalize supertype and mixins so that supertype is always a class, not a trait. @@ -1723,7 +1723,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { * @param rhs ... */ def computeParamAliases(clazz: Symbol, vparamss: List[List[ValDef]], rhs: Tree) { - debuglog("computing param aliases for "+clazz+":"+clazz.primaryConstructor.tpe+":"+rhs)//debug + log("computing param aliases for "+clazz+":"+clazz.primaryConstructor.tpe+":"+rhs)//debug def decompose(call: Tree): (Tree, List[Tree]) = call match { case Apply(fn, args) => val (superConstr, args1) = decompose(fn) @@ -1902,11 +1902,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { transformedOrTyped(ddef.rhs, EXPRmode, tpt1.tpe) } - if (meth.isPrimaryConstructor && meth.isClassConstructor && !isPastTyper && !reporter.hasErrors && !meth.owner.isSubClass(AnyValClass)) { - // At this point in AnyVal there is no supercall, which will blow up - // in computeParamAliases; there's nothing to be computed for Anyval anyway. - computeParamAliases(meth.owner, vparamss1, rhs1) - } + if (meth.isPrimaryConstructor && meth.isClassConstructor && !isPastTyper && !reporter.hasErrors && !meth.owner.isSubClass(AnyValClass)) { + // At this point in AnyVal there is no supercall, which will blow up + // in computeParamAliases; there's nothing to be computed for Anyval anyway. + computeParamAliases(meth.owner, vparamss1, rhs1) + } + if (tpt1.tpe.typeSymbol != NothingClass && !context.returnsSeen && rhs1.tpe.typeSymbol != NothingClass) rhs1 = checkDead(rhs1) @@ -1925,6 +1926,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } if (meth.isStructuralRefinementMember) checkMethodStructuralCompatible(meth) + treeCopy.DefDef(ddef, typedMods, ddef.name, tparams1, vparamss1, tpt1, rhs1) setType NoType } @@ -3264,6 +3266,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } def typedAnnotated(ann: Tree, arg1: Tree): Tree = { + def mkTypeTree(tpe: Type) = TypeTree(tpe) setOriginal tree setPos tree.pos.focus /** mode for typing the annotation itself */ val annotMode = mode & ~TYPEmode | EXPRmode @@ -3309,19 +3312,20 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { arg1 // simply drop erroneous annotations else { ann.tpe = atype - TypeTree(atype) setOriginal tree + mkTypeTree(atype) } } else { // the annotation was typechecked before - TypeTree(ann.tpe) setOriginal tree + mkTypeTree(ann.tpe) } - } else { + } + else { if (ann.tpe == null) { val annotInfo = typedAnnotation(ann, annotMode) ann.tpe = arg1.tpe.withAnnotation(annotInfo) } val atype = ann.tpe - Typed(arg1, TypeTree(atype) setOriginal tree setPos tree.pos.focus) setPos tree.pos setType atype + Typed(arg1, mkTypeTree(atype)) setPos tree.pos setType atype } } @@ -3626,7 +3630,6 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val Select(qual, name) = fun tryTypedArgs(args, forArgMode(fun, mode)) match { case Some(args1) => - assert((args1.length == 0) || !args1.head.tpe.isErroneous, "try typed args is ok") val qual1 = if (!pt.isError) adaptToArguments(qual, name, args1, pt, true, true) else qual diff --git a/src/compiler/scala/tools/nsc/util/DocStrings.scala b/src/compiler/scala/tools/nsc/util/DocStrings.scala index fbe92e5d84..f4ce6d6ef1 100755 --- a/src/compiler/scala/tools/nsc/util/DocStrings.scala +++ b/src/compiler/scala/tools/nsc/util/DocStrings.scala @@ -26,6 +26,14 @@ object DocStrings { if (start < str.length && isIdentifierPart(str charAt start)) skipIdent(str, start + 1) else start + /** Returns index of string `str` following `start` skipping + * sequence of identifier characters. + */ + def skipTag(str: String, start: Int): Int = + if (start < str.length && (str charAt start) == '@') skipIdent(str, start + 1) + else start + + /** Returns index of string `str` after `start` skipping longest * sequence of space and tab characters, possibly also containing * a single `*` character or the `/``**` sequence. @@ -68,38 +76,46 @@ object DocStrings { /** Produces a string index, which is a list of ``sections'', i.e * pairs of start/end positions of all tagged sections in the string. - * Every section starts with a `@` and extends to the next `@`, or - * to the end of the comment string, but excluding the final two + * Every section starts with an at sign and extends to the next at sign, + * or to the end of the comment string, but excluding the final two * characters which terminate the comment. * * Also take usecases into account - they need to expand until the next * usecase or the end of the string, as they might include other sections * of their own */ - def tagIndex(str: String, p: Int => Boolean = (idx => true)): List[(Int, Int)] = - findAll(str, 0) (idx => str(idx) == '@' && p(idx)) match { + def tagIndex(str: String, p: Int => Boolean = (idx => true)): List[(Int, Int)] = { + var indices = findAll(str, 0) (idx => str(idx) == '@' && p(idx)) + indices = mergeUsecaseSections(str, indices) + indices = mergeInheritdocSections(str, indices) + + indices match { case List() => List() - case idxs => { - val idxs2 = mergeUsecaseSections(str, idxs) - idxs2 zip (idxs2.tail ::: List(str.length - 2)) - } + case idxs => idxs zip (idxs.tail ::: List(str.length - 2)) } + } /** * Merge sections following an usecase into the usecase comment, so they * can override the parent symbol's sections */ def mergeUsecaseSections(str: String, idxs: List[Int]): List[Int] = { - idxs.find(str.substring(_).startsWith("@usecase")) match { - case Some(firstUC) => - val commentSections = idxs.take(idxs.indexOf(firstUC)) - val usecaseSections = idxs.drop(idxs.indexOf(firstUC)).filter(str.substring(_).startsWith("@usecase")) + idxs.indexWhere(str.substring(_).startsWith("@usecase")) match { + case firstUCIndex if firstUCIndex != -1 => + val commentSections = idxs.take(firstUCIndex) + val usecaseSections = idxs.drop(firstUCIndex).filter(str.substring(_).startsWith("@usecase")) commentSections ::: usecaseSections - case None => + case _ => idxs } } + /** + * Merge the inheritdoc sections, as they never make sense on their own + */ + def mergeInheritdocSections(str: String, idxs: List[Int]): List[Int] = + idxs.filterNot(str.substring(_).startsWith("@inheritdoc")) + /** Does interval `iv` start with given `tag`? */ def startsWithTag(str: String, section: (Int, Int), tag: String): Boolean = @@ -108,12 +124,11 @@ object DocStrings { def startsWithTag(str: String, start: Int, tag: String): Boolean = str.startsWith(tag, start) && !isIdentifierPart(str charAt (start + tag.length)) - /** The first start tag of a list of tag intervals, * or the end of the whole comment string - 2 if list is empty */ def startTag(str: String, sections: List[(Int, Int)]) = sections match { - case List() => str.length - 2 + case Nil => str.length - 2 case (start, _) :: _ => start } @@ -155,4 +170,46 @@ object DocStrings { idx } } + + /** A map from the section tag to section parameters */ + def sectionTagMap(str: String, sections: List[(Int, Int)]): Map[String, (Int, Int)] = + Map() ++ { + for (section <- sections) yield + extractSectionTag(str, section) -> section + } + + /** Extract the section tag, treating the section tag as an indentifier */ + def extractSectionTag(str: String, section: (Int, Int)): String = + str.substring(section._1, skipTag(str, section._1)) + + /** Extract the section parameter */ + def extractSectionParam(str: String, section: (Int, Int)): String = { + assert(str.substring(section._1).startsWith("@param") || + str.substring(section._1).startsWith("@tparam") || + str.substring(section._1).startsWith("@throws")) + + val start = skipWhitespace(str, skipTag(str, section._1)) + val finish = skipIdent(str, start) + + str.substring(start, finish) + } + + /** Extract the section text, except for the tag and comment newlines */ + def extractSectionText(str: String, section: (Int, Int)): (Int, Int) = { + if (str.substring(section._1).startsWith("@param") || + str.substring(section._1).startsWith("@tparam") || + str.substring(section._1).startsWith("@throws")) + (skipWhitespace(str, skipIdent(str, skipWhitespace(str, skipTag(str, section._1)))), section._2) + else + (skipWhitespace(str, skipTag(str, section._1)), section._2) + } + + /** Cleanup section text */ + def cleanupSectionText(str: String) = { + var result = str.trim.replaceAll("\n\\s+\\*\\s+", " \n") + while (result.endsWith("\n")) + result = result.substring(0, str.length - 1) + result + } + } |