diff options
Diffstat (limited to 'src')
99 files changed, 1765 insertions, 1254 deletions
diff --git a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala index bdcc7383b0..31974b5b76 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala @@ -45,7 +45,9 @@ trait GenTrees { case global.EmptyTree => reifyMirrorObject(EmptyTree) case global.emptyValDef => - mirrorBuildSelect(nme.emptyValDef) + mirrorSelect(nme.emptyValDef) + case global.pendingSuperCall => + mirrorSelect(nme.pendingSuperCall) case FreeDef(_, _, _, _, _) => reifyNestedFreeDef(tree) case FreeRef(_, _) => @@ -101,7 +103,8 @@ trait GenTrees { case ReifiedTree(_, _, inlinedSymtab, rtree, _, _, _) => if (reifyDebug) println("inlining the splicee") // all free vars local to the enclosing reifee should've already been inlined by ``Metalevels'' - inlinedSymtab.syms foreach (sym => if (sym.isLocalToReifee) assert(false, inlinedSymtab.symDef(sym))) + for (sym <- inlinedSymtab.syms if sym.isLocalToReifee) + abort("local free var, should have already been inlined by Metalevels: " + inlinedSymtab.symDef(sym)) state.symtab ++= inlinedSymtab rtree case tree => @@ -117,88 +120,102 @@ trait GenTrees { // unlike in `reifyBoundType` we can skip checking for `tpe` being local or not local w.r.t the reifee // a single check for a symbol of the bound term should be enough // that's because only Idents and Thises can be bound terms, and they cannot host complex types - private def reifyBoundTerm(tree: Tree): Tree = tree match { - case tree @ This(_) if tree.symbol == NoSymbol => - throw new Error("unexpected: bound term that doesn't have a symbol: " + showRaw(tree)) - case tree @ This(_) if tree.symbol.isClass && !tree.symbol.isModuleClass && !tree.symbol.isLocalToReifee => - val sym = tree.symbol - if (reifyDebug) println("This for %s, reified as freeVar".format(sym)) - if (reifyDebug) println("Free: " + sym) - mirrorBuildCall(nme.Ident, reifyFreeTerm(This(sym))) - case tree @ This(_) if !tree.symbol.isLocalToReifee => - if (reifyDebug) println("This for %s, reified as This".format(tree.symbol)) - mirrorBuildCall(nme.This, reify(tree.symbol)) - case tree @ This(_) if tree.symbol.isLocalToReifee => - mirrorCall(nme.This, reify(tree.qual)) - case tree @ Ident(_) if tree.symbol == NoSymbol => - // this sometimes happens, e.g. for binds that don't have a body - // or for untyped code generated during previous phases - // (see a comment in Reifiers about the latter, starting with "why do we resetAllAttrs?") - mirrorCall(nme.Ident, reify(tree.name)) - case tree @ Ident(_) if !tree.symbol.isLocalToReifee => - if (tree.symbol.isVariable && tree.symbol.owner.isTerm) { - captureVariable(tree.symbol) // Note order dependency: captureVariable needs to come before reification here. - mirrorCall(nme.Select, mirrorBuildCall(nme.Ident, reify(tree.symbol)), reify(nme.elem)) - } else { - mirrorBuildCall(nme.Ident, reify(tree.symbol)) - } - case tree @ Ident(_) if tree.symbol.isLocalToReifee => - mirrorCall(nme.Ident, reify(tree.name)) - case _ => - throw new Error("internal error: %s (%s, %s) is not supported".format(tree, tree.productPrefix, tree.getClass)) + private def reifyBoundTerm(tree: Tree): Tree = { + val sym = tree.symbol + + tree match { + case This(qual) => + assert(sym != NoSymbol, "unexpected: bound term that doesn't have a symbol: " + showRaw(tree)) + if (sym.isLocalToReifee) + mirrorCall(nme.This, reify(qual)) + else if (sym.isClass && !sym.isModuleClass) { + if (reifyDebug) println("This for %s, reified as freeVar".format(sym)) + if (reifyDebug) println("Free: " + sym) + mirrorBuildCall(nme.Ident, reifyFreeTerm(This(sym))) + } + else { + if (reifyDebug) println("This for %s, reified as This".format(sym)) + mirrorBuildCall(nme.This, reify(sym)) + } + + case Ident(name) => + if (sym == NoSymbol) { + // this sometimes happens, e.g. for binds that don't have a body + // or for untyped code generated during previous phases + // (see a comment in Reifiers about the latter, starting with "why do we resetAllAttrs?") + mirrorCall(nme.Ident, reify(name)) + } + else if (!sym.isLocalToReifee) { + if (sym.isVariable && sym.owner.isTerm) { + captureVariable(sym) // Note order dependency: captureVariable needs to come before reification here. + mirrorCall(nme.Select, mirrorBuildCall(nme.Ident, reify(sym)), reify(nme.elem)) + } + else mirrorBuildCall(nme.Ident, reify(sym)) + } + else mirrorCall(nme.Ident, reify(name)) + + case Select(qual, name) => + if (sym == NoSymbol || sym.name == name) + reifyProduct(tree) + else + reifyProduct(Select(qual, sym.name)) + + case _ => + throw new Error("internal error: %s (%s, %s) is not supported".format(tree, tree.productPrefix, tree.getClass)) + } } private def reifyBoundType(tree: Tree): Tree = { + val sym = tree.symbol + val tpe = tree.tpe + def reifyBoundType(tree: Tree): Tree = { - if (tree.tpe == null) - throw new Error("unexpected: bound type that doesn't have a tpe: " + showRaw(tree)) + assert(tpe != null, "unexpected: bound type that doesn't have a tpe: " + showRaw(tree)) // if a symbol or a type of the scrutinee are local to reifee // (e.g. point to a locally declared class or to a path-dependent thingie that depends on a local variable) // then we can reify the scrutinee as a symless AST and that will definitely be hygienic // why? because then typechecking of a scrutinee doesn't depend on the environment external to the quasiquote // otherwise we need to reify the corresponding type - if (tree.symbol.isLocalToReifee || tree.tpe.isLocalToReifee) + if (sym.isLocalToReifee || tpe.isLocalToReifee) reifyProduct(tree) else { - val sym = tree.symbol - val tpe = tree.tpe if (reifyDebug) println("reifying bound type %s (underlying type is %s)".format(sym, tpe)) if (tpe.isSpliceable) { val spliced = spliceType(tpe) + if (spliced == EmptyTree) { if (reifyDebug) println("splicing failed: reify as is") mirrorBuildCall(nme.TypeTree, reify(tpe)) - } else { - spliced match { - case TypeRefToFreeType(freeType) => - if (reifyDebug) println("splicing returned a free type: " + freeType) - Ident(freeType) - case _ => - if (reifyDebug) println("splicing succeeded: " + spliced) - mirrorBuildCall(nme.TypeTree, spliced) - } } - } else { - if (sym.isLocatable) { - if (reifyDebug) println("tpe is locatable: reify as Ident(%s)".format(sym)) - mirrorBuildCall(nme.Ident, reify(sym)) - } else { - if (reifyDebug) println("tpe is not locatable: reify as TypeTree(%s)".format(tpe)) - mirrorBuildCall(nme.TypeTree, reify(tpe)) + else spliced match { + case TypeRefToFreeType(freeType) => + if (reifyDebug) println("splicing returned a free type: " + freeType) + Ident(freeType) + case _ => + if (reifyDebug) println("splicing succeeded: " + spliced) + mirrorBuildCall(nme.TypeTree, spliced) } } + else if (sym.isLocatable) { + if (reifyDebug) println("tpe is locatable: reify as Ident(%s)".format(sym)) + mirrorBuildCall(nme.Ident, reify(sym)) + } + else { + if (reifyDebug) println("tpe is not locatable: reify as TypeTree(%s)".format(tpe)) + mirrorBuildCall(nme.TypeTree, reify(tpe)) + } } } tree match { - case Select(_, _) => - reifyBoundType(tree) - case SelectFromTypeTree(_, _) => - reifyBoundType(tree) - case Ident(_) => + case Select(qual, name) if name != sym.name => + reifyBoundType(Select(qual, sym.name)) + + case Select(_, _) | SelectFromTypeTree(_, _) | Ident(_) => reifyBoundType(tree) + case _ => throw new Error("internal error: %s (%s, %s) is not supported".format(tree, tree.productPrefix, tree.getClass)) } diff --git a/src/compiler/scala/reflect/reify/codegen/GenUtils.scala b/src/compiler/scala/reflect/reify/codegen/GenUtils.scala index 49877b4286..21db93d8f5 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenUtils.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenUtils.scala @@ -34,9 +34,6 @@ trait GenUtils { def mirrorSelect(name: String): Tree = termPath(nme.UNIVERSE_PREFIX + name) - def mirrorBuildSelect(name: String): Tree = - termPath(nme.UNIVERSE_BUILD_PREFIX + name) - def mirrorMirrorSelect(name: String): Tree = termPath(nme.MIRROR_PREFIX + name) diff --git a/src/compiler/scala/reflect/reify/utils/Extractors.scala b/src/compiler/scala/reflect/reify/utils/Extractors.scala index b60d15c1d4..ebbcb95481 100644 --- a/src/compiler/scala/reflect/reify/utils/Extractors.scala +++ b/src/compiler/scala/reflect/reify/utils/Extractors.scala @@ -251,6 +251,8 @@ trait Extractors { object BoundTerm { def unapply(tree: Tree): Option[Tree] = tree match { + case Select(_, name) if name.isTermName => + Some(tree) case Ident(name) if name.isTermName => Some(tree) case This(_) => diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index 4e7ba60d5e..355a1fd262 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -23,7 +23,7 @@ trait CompilationUnits { self: Global => /** One unit of compilation that has been submitted to the compiler. * It typically corresponds to a single file of source code. It includes * error-reporting hooks. */ - class CompilationUnit(val source: SourceFile) extends CompilationUnitContextApi { + class CompilationUnit(val source: SourceFile) extends CompilationUnitContextApi { self => /** the fresh name creator */ var fresh: FreshNameCreator = new FreshNameCreator.Default @@ -57,7 +57,23 @@ trait CompilationUnits { self: Global => /** Synthetic definitions generated by namer, eliminated by typer. */ - val synthetics = mutable.HashMap[Symbol, Tree]() + object synthetics { + private val map = mutable.HashMap[Symbol, Tree]() + def update(sym: Symbol, tree: Tree) { + debuglog(s"adding synthetic ($sym, $tree) to $self") + map.update(sym, tree) + } + def -=(sym: Symbol) { + debuglog(s"removing synthetic $sym from $self") + map -= sym + } + def get(sym: Symbol): Option[Tree] = logResultIf[Option[Tree]](s"found synthetic for $sym in $self", _.isDefined) { + map get sym + } + def keys: Iterable[Symbol] = map.keys + def clear(): Unit = map.clear() + override def toString = map.toString + } /** things to check at end of compilation unit */ val toCheck = new ListBuffer[() => Unit] diff --git a/src/compiler/scala/tools/nsc/Driver.scala b/src/compiler/scala/tools/nsc/Driver.scala index 1775602122..814bd58a66 100644 --- a/src/compiler/scala/tools/nsc/Driver.scala +++ b/src/compiler/scala/tools/nsc/Driver.scala @@ -54,10 +54,10 @@ abstract class Driver { doCompile(compiler) } catch { case ex: Throwable => - compiler.logThrowable(ex) + compiler.reportThrowable(ex) ex match { - case FatalError(msg) => reporter.error(null, "fatal error: " + msg) - case _ => throw ex + case FatalError(msg) => // signals that we should fail compilation. + case _ => throw ex // unexpected error, tell the outside world. } } } diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index b0288c0149..757ac94dbd 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -292,7 +292,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter) log(msg) } - def logThrowable(t: Throwable): Unit = globalError(throwableAsString(t)) + @deprecated("Renamed to reportThrowable", "2.10.1") + def logThrowable(t: Throwable): Unit = reportThrowable(t) + def reportThrowable(t: Throwable): Unit = globalError(throwableAsString(t)) override def throwableAsString(t: Throwable) = util.stackTraceString(t) // ------------ File interface ----------------------------------------- @@ -777,7 +779,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /* The set of phase objects that is the basis for the compiler phase chain */ protected lazy val phasesSet = new mutable.HashSet[SubComponent] protected lazy val phasesDescMap = new mutable.HashMap[SubComponent, String] withDefaultValue "" - private lazy val phaseTimings = new Phases.TimingModel // tracking phase stats protected def addToPhasesSet(sub: SubComponent, descr: String) { phasesSet += sub @@ -1144,37 +1145,41 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** Don't want to introduce new errors trying to report errors, * so swallow exceptions. */ - override def supplementErrorMessage(errorMessage: String): String = try { - val tree = analyzer.lastTreeToTyper - val sym = tree.symbol - val tpe = tree.tpe - val enclosing = lastSeenContext.enclClassOrMethod.tree - - val info1 = formatExplain( - "while compiling" -> currentSource.path, - "during phase" -> ( if (globalPhase eq phase) phase else "global=%s, atPhase=%s".format(globalPhase, phase) ), - "library version" -> scala.util.Properties.versionString, - "compiler version" -> Properties.versionString, - "reconstructed args" -> settings.recreateArgs.mkString(" ") - ) - val info2 = formatExplain( - "last tree to typer" -> tree.summaryString, - "symbol" -> Option(sym).fold("null")(_.debugLocationString), - "symbol definition" -> Option(sym).fold("null")(_.defString), - "tpe" -> tpe, - "symbol owners" -> ownerChainString(sym), - "context owners" -> ownerChainString(lastSeenContext.owner) - ) - val info3: List[String] = ( - ( List("== Enclosing template or block ==", nodePrinters.nodeToString(enclosing).trim) ) - ++ ( if (tpe eq null) Nil else List("== Expanded type of tree ==", typeDeconstruct.show(tpe)) ) - ++ ( if (!opt.debug) Nil else List("== Current unit body ==", nodePrinters.nodeToString(currentUnit.body)) ) - ++ ( List(errorMessage) ) - ) - - ("\n" + info1) :: info2 :: info3 mkString "\n\n" - } - catch { case x: Exception => errorMessage } + override def supplementErrorMessage(errorMessage: String): String = + if (currentRun.supplementedError) errorMessage + else try { + val tree = analyzer.lastTreeToTyper + val sym = tree.symbol + val tpe = tree.tpe + val enclosing = lastSeenContext.enclClassOrMethod.tree + + val info1 = formatExplain( + "while compiling" -> currentSource.path, + "during phase" -> ( if (globalPhase eq phase) phase else "global=%s, atPhase=%s".format(globalPhase, phase) ), + "library version" -> scala.util.Properties.versionString, + "compiler version" -> Properties.versionString, + "reconstructed args" -> settings.recreateArgs.mkString(" ") + ) + val info2 = formatExplain( + "last tree to typer" -> tree.summaryString, + "symbol" -> Option(sym).fold("null")(_.debugLocationString), + "symbol definition" -> Option(sym).fold("null")(_.defString), + "tpe" -> tpe, + "symbol owners" -> ownerChainString(sym), + "context owners" -> ownerChainString(lastSeenContext.owner) + ) + val info3: List[String] = ( + ( List("== Enclosing template or block ==", nodePrinters.nodeToString(enclosing).trim) ) + ++ ( if (tpe eq null) Nil else List("== Expanded type of tree ==", typeDeconstruct.show(tpe)) ) + ++ ( if (!opt.debug) Nil else List("== Current unit body ==", nodePrinters.nodeToString(currentUnit.body)) ) + ++ ( List(errorMessage) ) + ) + + currentRun.supplementedError = true + + ("\n" + info1) :: info2 :: info3 mkString "\n\n" + } + catch { case x: Exception => errorMessage } /** The id of the currently active run */ @@ -1230,6 +1235,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** Has any macro expansion used a fallback during this run? */ var seenMacroExpansionsFallingBack = false + /** Have we already supplemented the error message of a compiler crash? */ + private[nsc] final var supplementedError = false + /** To be initialized from firstPhase. */ private var terminalPhase: Phase = NoPhase @@ -1575,7 +1583,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // progress update informTime(globalPhase.description, startTime) - phaseTimings(globalPhase) = currentTime - startTime if (opt.writeICodeAtICode || (opt.printPhase && runIsAtOptimiz)) { // Write *.icode files when -Xprint-icode or -Xprint:<some-optimiz-phase> was given. diff --git a/src/compiler/scala/tools/nsc/Phases.scala b/src/compiler/scala/tools/nsc/Phases.scala index c914344fd5..0901ade2d7 100644 --- a/src/compiler/scala/tools/nsc/Phases.scala +++ b/src/compiler/scala/tools/nsc/Phases.scala @@ -9,6 +9,7 @@ import symtab.Flags import scala.reflect.internal.util.TableDef import scala.language.postfixOps +@deprecated("Scheduled for removal as being a dead-code in the compiler.", "2.10.1") object Phases { val MaxPhases = 64 diff --git a/src/compiler/scala/tools/nsc/ast/DocComments.scala b/src/compiler/scala/tools/nsc/ast/DocComments.scala index 5a4be5125d..e635c5e87d 100755 --- a/src/compiler/scala/tools/nsc/ast/DocComments.scala +++ b/src/compiler/scala/tools/nsc/ast/DocComments.scala @@ -19,11 +19,17 @@ import scala.collection.mutable */ trait DocComments { self: Global => - var cookedDocComments = Map[Symbol, String]() + val cookedDocComments = mutable.HashMap[Symbol, String]() /** The raw doc comment map */ val docComments = mutable.HashMap[Symbol, DocComment]() + def clearDocComments() { + cookedDocComments.clear() + docComments.clear() + defs.clear() + } + /** Associate comment with symbol `sym` at position `pos`. */ def docComment(sym: Symbol, docStr: String, pos: Position = NoPosition) = if ((sym ne null) && (sym ne NoSymbol)) @@ -55,25 +61,20 @@ trait DocComments { self: Global => * 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 = cookedDocComments.get(sym) match { - case Some(comment) => - comment - case None => - val ownComment = if (docStr.length == 0) docComments get sym map (_.template) getOrElse "" + def cookedDocComment(sym: Symbol, docStr: String = ""): String = cookedDocComments.getOrElseUpdate(sym, { + 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 - } + 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) + } + }) /** The cooked doc comment of symbol `sym` after variable expansion, or "" if missing. * diff --git a/src/compiler/scala/tools/nsc/ast/Positions.scala b/src/compiler/scala/tools/nsc/ast/Positions.scala index d8fb632f73..49569f5e05 100644 --- a/src/compiler/scala/tools/nsc/ast/Positions.scala +++ b/src/compiler/scala/tools/nsc/ast/Positions.scala @@ -20,7 +20,7 @@ trait Positions extends scala.reflect.internal.Positions { // 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)) () + if (!c.canHaveAttrs) () else if (c.pos == NoPosition) { reporter.warning(t.pos, " Positioned tree has unpositioned child in phase " + globalPhase) inform("parent: " + treeSymStatus(t)) diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index 53d35791b6..5cb43575b8 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -58,7 +58,7 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { def mkUnchecked(expr: Tree): Tree = atPos(expr.pos) { // This can't be "Annotated(New(UncheckedClass), expr)" because annotations // are very picky about things and it crashes the compiler with "unexpected new". - Annotated(New(scalaDot(UncheckedClass.name), ListOfNil), expr) + Annotated(New(scalaDot(UncheckedClass.name), Nil), expr) } // if it's a Match, mark the selector unchecked; otherwise nothing. def mkUncheckedMatch(tree: Tree) = tree match { diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index 296d55fec5..54402f0903 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -65,6 +65,13 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => // --- factory methods ---------------------------------------------------------- + /** Factory method for a primary constructor super call `super.<init>(args_1)...(args_n)` + */ + def PrimarySuperCall(argss: List[List[Tree]]): Tree = argss match { + case Nil => Apply(gen.mkSuperSelect, Nil) + case xs :: rest => rest.foldLeft(Apply(gen.mkSuperSelect, xs): Tree)(Apply.apply) + } + /** Generates a template with constructor corresponding to * * constrmods (vparams1_) ... (vparams_n) preSuper { presupers } @@ -82,7 +89,7 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => * body * } */ - def Template(parents: List[Tree], self: ValDef, constrMods: Modifiers, vparamss: List[List[ValDef]], argss: List[List[Tree]], body: List[Tree], superPos: Position): Template = { + def Template(parents: List[Tree], self: ValDef, constrMods: Modifiers, vparamss: List[List[ValDef]], body: List[Tree], superPos: Position): Template = { /* Add constructor to template */ // create parameters for <init> as synthetic trees. @@ -117,9 +124,16 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => if (vparamss1.isEmpty || !vparamss1.head.isEmpty && vparamss1.head.head.mods.isImplicit) vparamss1 = List() :: vparamss1; val superRef: Tree = atPos(superPos)(gen.mkSuperSelect) - val superCall = (superRef /: argss) (Apply.apply) + val superCall = pendingSuperCall // we can't know in advance which of the parents will end up as a superclass + // this requires knowing which of the parents is a type macro and which is not + // and that's something that cannot be found out before typer + // (the type macros aren't in the trunk yet, but there is a plan for them to land there soon) + // this means that we don't know what will be the arguments of the super call + // therefore here we emit a dummy which gets populated when the template is named and typechecked List( - atPos(wrappingPos(superPos, lvdefs ::: argss.flatten)) ( + // TODO: previously this was `wrappingPos(superPos, lvdefs ::: argss.flatten)` + // is it going to be a problem that we can no longer include the `argss`? + atPos(wrappingPos(superPos, lvdefs)) ( DefDef(constrMods, nme.CONSTRUCTOR, List(), vparamss1, TypeTree(), Block(lvdefs ::: List(superCall), Literal(Constant()))))) } } @@ -137,11 +151,10 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => * @param constrMods the modifiers for the class constructor, i.e. as in `class C private (...)` * @param vparamss the value parameters -- if they have symbols they * should be owned by `sym` - * @param argss the supercall arguments * @param body the template statements without primary constructor * and value parameter fields. */ - def ClassDef(sym: Symbol, constrMods: Modifiers, vparamss: List[List[ValDef]], argss: List[List[Tree]], body: List[Tree], superPos: Position): ClassDef = { + def ClassDef(sym: Symbol, constrMods: Modifiers, vparamss: List[List[ValDef]], body: List[Tree], superPos: Position): ClassDef = { // "if they have symbols they should be owned by `sym`" assert( mforall(vparamss)(p => (p.symbol eq NoSymbol) || (p.symbol.owner == sym)), @@ -151,7 +164,7 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => ClassDef(sym, Template(sym.info.parents map TypeTree, if (sym.thisSym == sym || phase.erasedTypes) emptyValDef else ValDef(sym.thisSym), - constrMods, vparamss, argss, body, superPos)) + constrMods, vparamss, body, superPos)) } // --- subcomponents -------------------------------------------------- @@ -324,6 +337,8 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => else super.transform { tree match { + case tree if !tree.canHaveAttrs => + tree case tpt: TypeTree => if (tpt.original != null) transform(tpt.original) @@ -337,8 +352,6 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => transform(fn) case This(_) if tree.symbol != null && tree.symbol.isPackageClass => tree - case EmptyTree => - tree case _ => val dupl = tree.duplicate if (tree.hasSymbol && (!localOnly || (locals contains tree.symbol)) && !(keepLabels && tree.symbol.isLabel)) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 074fcabec8..33db4ee2d5 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1562,9 +1562,9 @@ self => val nstart = in.skipToken() val npos = r2p(nstart, nstart, in.lastOffset) val tstart = in.offset - val (parents, argss, self, stats) = template(isTrait = false) + val (parents, self, stats) = template() val cpos = r2p(tstart, tstart, in.lastOffset max tstart) - makeNew(parents, self, stats, argss, npos, cpos) + makeNew(parents, self, stats, npos, cpos) case _ => syntaxErrorOrIncomplete("illegal start of simple expression", true) errorTermTree @@ -2106,7 +2106,7 @@ self => def annotationExpr(): Tree = atPos(in.offset) { val t = exprSimpleType() if (in.token == LPAREN) New(t, multipleArgumentExprs()) - else New(t, ListOfNil) + else New(t, Nil) } /* -------- PARAMETERS ------------------------------------------- */ @@ -2742,20 +2742,17 @@ self => * TraitParents ::= AnnotType {with AnnotType} * }}} */ - def templateParents(isTrait: Boolean): (List[Tree], List[List[Tree]]) = { - val parents = new ListBuffer[Tree] += startAnnotType() - val argss = ( - // TODO: the insertion of ListOfNil here is where "new Foo" becomes - // indistinguishable from "new Foo()". - if (in.token == LPAREN && !isTrait) multipleArgumentExprs() - else ListOfNil - ) - - while (in.token == WITH) { - in.nextToken() - parents += startAnnotType() + def templateParents(): List[Tree] = { + val parents = new ListBuffer[Tree] + def readAppliedParent() = { + val start = in.offset + val parent = startAnnotType() + val argss = if (in.token == LPAREN) multipleArgumentExprs() else Nil + parents += atPos(start)((parent /: argss)(Apply.apply)) } - (parents.toList, argss) + readAppliedParent() + while (in.token == WITH) { in.nextToken(); readAppliedParent() } + parents.toList } /** {{{ @@ -2765,12 +2762,12 @@ self => * EarlyDef ::= Annotations Modifiers PatDef * }}} */ - def template(isTrait: Boolean): (List[Tree], List[List[Tree]], ValDef, List[Tree]) = { + def template(): (List[Tree], ValDef, List[Tree]) = { newLineOptWhenFollowedBy(LBRACE) if (in.token == LBRACE) { // @S: pre template body cannot stub like post body can! val (self, body) = templateBody(isPre = true) - if (in.token == WITH && self.isEmpty) { + if (in.token == WITH && (self eq emptyValDef)) { val earlyDefs: List[Tree] = body flatMap { case vdef @ ValDef(mods, _, _, _) if !mods.isDeferred => List(copyValDef(vdef)(mods = mods | Flags.PRESUPER)) @@ -2782,16 +2779,16 @@ self => case _ => List() } in.nextToken() - val (parents, argss) = templateParents(isTrait = isTrait) - val (self1, body1) = templateBodyOpt(traitParentSeen = isTrait) - (parents, argss, self1, earlyDefs ::: body1) + val parents = templateParents() + val (self1, body1) = templateBodyOpt(parenMeansSyntaxError = false) + (parents, self1, earlyDefs ::: body1) } else { - (List(), ListOfNil, self, body) + (List(), self, body) } } else { - val (parents, argss) = templateParents(isTrait = isTrait) - val (self, body) = templateBodyOpt(traitParentSeen = isTrait) - (parents, argss, self, body) + val parents = templateParents() + val (self, body) = templateBodyOpt(parenMeansSyntaxError = false) + (parents, self, body) } } @@ -2805,15 +2802,15 @@ self => * }}} */ def templateOpt(mods: Modifiers, name: Name, constrMods: Modifiers, vparamss: List[List[ValDef]], tstart: Int): Template = { - val (parents0, argss, self, body) = ( + val (parents0, self, body) = ( if (in.token == EXTENDS || in.token == SUBTYPE && mods.isTrait) { in.nextToken() - template(isTrait = mods.isTrait) + template() } else { newLineOptWhenFollowedBy(LBRACE) - val (self, body) = templateBodyOpt(traitParentSeen = false) - (List(), ListOfNil, self, body) + val (self, body) = templateBodyOpt(parenMeansSyntaxError = mods.isTrait || name.isTermName) + (List(), self, body) } ) def anyrefParents() = { @@ -2835,7 +2832,7 @@ self => if (inScalaRootPackage && ScalaValueClassNames.contains(name)) Template(parents0, self, anyvalConstructor :: body) else - Template(anyrefParents, self, constrMods, vparamss, argss, body, o2p(tstart)) + Template(anyrefParents, self, constrMods, vparamss, body, o2p(tstart)) } } @@ -2850,14 +2847,15 @@ self => case (self, Nil) => (self, EmptyTree.asList) case result => result } - def templateBodyOpt(traitParentSeen: Boolean): (ValDef, List[Tree]) = { + def templateBodyOpt(parenMeansSyntaxError: Boolean): (ValDef, List[Tree]) = { newLineOptWhenFollowedBy(LBRACE) if (in.token == LBRACE) { templateBody(isPre = false) } else { - if (in.token == LPAREN) - syntaxError((if (traitParentSeen) "parents of traits" else "traits or objects")+ - " may not have parameters", true) + if (in.token == LPAREN) { + if (parenMeansSyntaxError) syntaxError(s"traits or objects may not have parameters", true) + else abort("unexpected opening parenthesis") + } (emptyValDef, List()) } } diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index 0ac46a18bc..f94055f666 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -205,20 +205,26 @@ abstract class TreeBuilder { */ def makeAnonymousNew(stats: List[Tree]): Tree = { val stats1 = if (stats.isEmpty) List(Literal(Constant(()))) else stats - makeNew(Nil, emptyValDef, stats1, ListOfNil, NoPosition, NoPosition) + makeNew(Nil, emptyValDef, stats1, NoPosition, NoPosition) } /** Create positioned tree representing an object creation <new parents { stats } * @param npos the position of the new * @param cpos the position of the anonymous class starting with parents */ - def makeNew(parents: List[Tree], self: ValDef, stats: List[Tree], argss: List[List[Tree]], + def makeNew(parents: List[Tree], self: ValDef, stats: List[Tree], npos: Position, cpos: Position): Tree = if (parents.isEmpty) - makeNew(List(scalaAnyRefConstr), self, stats, argss, npos, cpos) - else if (parents.tail.isEmpty && stats.isEmpty) - atPos(npos union cpos) { New(parents.head, argss) } - else { + makeNew(List(scalaAnyRefConstr), self, stats, npos, cpos) + else if (parents.tail.isEmpty && stats.isEmpty) { + // `Parsers.template` no longer differentiates tpts and their argss + // e.g. `C()` will be represented as a single tree Apply(Ident(C), Nil) + // instead of parents = Ident(C), argss = Nil as before + // this change works great for things that are actually templates + // but in this degenerate case we need to perform postprocessing + val app = treeInfo.dissectApplied(parents.head) + atPos(npos union cpos) { New(app.callee, app.argss) } + } else { val x = tpnme.ANON_CLASS_NAME atPos(npos union cpos) { Block( @@ -226,12 +232,12 @@ abstract class TreeBuilder { atPos(cpos) { ClassDef( Modifiers(FINAL), x, Nil, - Template(parents, self, NoMods, ListOfNil, argss, stats, cpos.focus)) + Template(parents, self, NoMods, ListOfNil, stats, cpos.focus)) }), atPos(npos) { New( Ident(x) setPos npos.focus, - ListOfNil) + Nil) } ) } diff --git a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala index 068836fe4f..d50d4cd125 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala @@ -320,7 +320,12 @@ trait BasicBlocks { else instrs.zipWithIndex collect { case (oldInstr, i) if map contains oldInstr => - code.touched |= replaceInstruction(i, map(oldInstr)) + // SI-6288 clone important here because `replaceInstruction` assigns + // a position to `newInstr`. Without this, a single instruction can + // be added twice, and the position last position assigned clobbers + // all previous positions in other usages. + val newInstr = map(oldInstr).clone() + code.touched |= replaceInstruction(i, newInstr) } ////////////////////// Emit ////////////////////// diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index ea4e8475f9..fd2b11898c 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -753,7 +753,8 @@ abstract class GenICode extends SubComponent { } else ctx1.bb.emit(CONSTANT(Constant(false))) } else if (r.isValueType && cast) { - assert(false, tree) /* Erasure should have added an unboxing operation to prevent that. */ + /* Erasure should have added an unboxing operation to prevent that. */ + abort("should have been unboxed by erasure: " + tree) } else if (r.isValueType) { ctx.bb.emit(IS_INSTANCE(REFERENCE(definitions.boxedClass(r.toType.typeSymbol)))) } else { @@ -1257,7 +1258,7 @@ abstract class GenICode extends SubComponent { val sym = ( if (!tree.symbol.isPackageClass) tree.symbol else tree.symbol.info.member(nme.PACKAGE) match { - case NoSymbol => assert(false, "Cannot use package as value: " + tree) ; NoSymbol + case NoSymbol => abort("Cannot use package as value: " + tree) case s => debugwarn("Bug: found package class where package object expected. Converting.") ; s.moduleClass } ) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 6eb05c4402..d185ed0c34 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -21,7 +21,7 @@ import asm.Label * * Documentation at http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/2012Q2/GenASM.pdf */ -abstract class GenASM extends SubComponent with BytecodeWriters { +abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { import global._ import icodes._ import icodes.opcodes._ @@ -32,20 +32,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters { /** Create a new phase */ override def newPhase(p: Phase): Phase = new AsmPhase(p) - private def outputDirectory(sym: Symbol): AbstractFile = - settings.outputDirs outputDirFor beforeFlatten(sym.sourceFile) - - private def getFile(base: AbstractFile, clsName: String, suffix: String): AbstractFile = { - var dir = base - val pathParts = clsName.split("[./]").toList - for (part <- pathParts.init) { - dir = dir.subdirectoryNamed(part) - } - dir.fileNamed(pathParts.last + suffix) - } - private def getFile(sym: Symbol, clsName: String, suffix: String): AbstractFile = - getFile(outputDirectory(sym), clsName, suffix) - /** JVM code generation phase */ class AsmPhase(prev: Phase) extends ICodePhase(prev) { @@ -55,62 +41,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters { val BeanInfoAttr = rootMirror.getRequiredClass("scala.beans.BeanInfo") - def isJavaEntryPoint(icls: IClass) = { - val sym = icls.symbol - def fail(msg: String, pos: Position = sym.pos) = { - icls.cunit.warning(sym.pos, - sym.name + " has a main method with parameter type Array[String], but " + sym.fullName('.') + " will not be a runnable program.\n" + - " Reason: " + msg - // TODO: make this next claim true, if possible - // by generating valid main methods as static in module classes - // not sure what the jvm allows here - // + " You can still run the program by calling it as " + sym.javaSimpleName + " instead." - ) - false - } - def failNoForwarder(msg: String) = { - fail(msg + ", which means no static forwarder can be generated.\n") - } - val possibles = if (sym.hasModuleFlag) (sym.tpe nonPrivateMember nme.main).alternatives else Nil - val hasApproximate = possibles exists { m => - m.info match { - case MethodType(p :: Nil, _) => p.tpe.typeSymbol == ArrayClass - case _ => false - } - } - // At this point it's a module with a main-looking method, so either succeed or warn that it isn't. - hasApproximate && { - // Before erasure so we can identify generic mains. - beforeErasure { - val companion = sym.linkedClassOfClass - val companionMain = companion.tpe.member(nme.main) - - if (hasJavaMainMethod(companion)) - failNoForwarder("companion contains its own main method") - else if (companion.tpe.member(nme.main) != NoSymbol) - // this is only because forwarders aren't smart enough yet - failNoForwarder("companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)") - else if (companion.isTrait) - failNoForwarder("companion is a trait") - // Now either succeeed, or issue some additional warnings for things which look like - // attempts to be java main methods. - else possibles exists { m => - m.info match { - case PolyType(_, _) => - fail("main methods cannot be generic.") - case MethodType(params, res) => - if (res.typeSymbol :: params exists (_.isAbstractType)) - fail("main methods cannot refer to type parameters or abstract types.", m.pos) - else - isJavaMainMethod(m) || fail("main method must have exact signature (Array[String])Unit", m.pos) - case tp => - fail("don't know what this is: " + tp, m.pos) - } - } - } - } - } - private def initBytecodeWriter(entryPoints: List[IClass]): BytecodeWriter = { settings.outputDirs.getSingleOutput match { case Some(f) if f hasExtension "jar" => @@ -160,7 +90,14 @@ abstract class GenASM extends SubComponent with BytecodeWriters { } // For predictably ordered error messages. - var sortedClasses = classes.values.toList sortBy ("" + _.symbol.fullName) + var sortedClasses = classes.values.toList sortBy (_.symbol.fullName) + + // Warn when classes will overwrite one another on case-insensitive systems. + for ((_, v1 :: v2 :: _) <- sortedClasses groupBy (_.symbol.javaClassName.toString.toLowerCase)) { + v1.cunit.warning(v1.symbol.pos, + s"Class ${v1.symbol.javaClassName} differs only in case from ${v2.symbol.javaClassName}. " + + "Such classes will overwrite one another on case-insensitive filesystems.") + } debuglog("Created new bytecode generator for " + classes.size + " classes.") val bytecodeWriter = initBytecodeWriter(sortedClasses filter isJavaEntryPoint) @@ -1090,12 +1027,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters { // a plain class lacking companion module, for details see `isCandidateForForwarders`). // ----------------------------------------------------------------------------------------- - val ExcludedForwarderFlags = { - import Flags._ - // Should include DEFERRED but this breaks findMember. - ( CASE | SPECIALIZED | LIFTED | PROTECTED | STATIC | EXPANDEDNAME | BridgeAndPrivateFlags ) - } - /** Add a forwarder for method m. Used only from addForwarders(). */ private def addForwarder(isRemoteClass: Boolean, jclass: asm.ClassVisitor, module: Symbol, m: Symbol) { val moduleName = javaName(module) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index 3d29b2590e..fe0020e074 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -26,7 +26,7 @@ import scala.language.postfixOps * @version 1.0 * */ -abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with BytecodeWriters { +abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with BytecodeWriters with GenJVMASM { import global._ import icodes._ import icodes.opcodes._ @@ -37,20 +37,6 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with /** Create a new phase */ override def newPhase(p: Phase): Phase = new JvmPhase(p) - private def outputDirectory(sym: Symbol): AbstractFile = - settings.outputDirs outputDirFor beforeFlatten(sym.sourceFile) - - private def getFile(base: AbstractFile, clsName: String, suffix: String): AbstractFile = { - var dir = base - val pathParts = clsName.split("[./]").toList - for (part <- pathParts.init) { - dir = dir.subdirectoryNamed(part) - } - dir.fileNamed(pathParts.last + suffix) - } - private def getFile(sym: Symbol, clsName: String, suffix: String): AbstractFile = - getFile(outputDirectory(sym), clsName, suffix) - /** JVM code generation phase */ class JvmPhase(prev: Phase) extends ICodePhase(prev) { @@ -58,63 +44,6 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with override def erasedTypes = true def apply(cls: IClass) = sys.error("no implementation") - def isJavaEntryPoint(clasz: IClass) = { - val sym = clasz.symbol - def fail(msg: String, pos: Position = sym.pos) = { - clasz.cunit.warning(sym.pos, - sym.name + " has a main method with parameter type Array[String], but " + sym.fullName('.') + " will not be a runnable program.\n" + - " Reason: " + msg - // TODO: make this next claim true, if possible - // by generating valid main methods as static in module classes - // not sure what the jvm allows here - // + " You can still run the program by calling it as " + sym.javaSimpleName + " instead." - ) - false - } - def failNoForwarder(msg: String) = { - fail(msg + ", which means no static forwarder can be generated.\n") - } - val possibles = if (sym.hasModuleFlag) (sym.tpe nonPrivateMember nme.main).alternatives else Nil - val hasApproximate = possibles exists { m => - m.info match { - case MethodType(p :: Nil, _) => p.tpe.typeSymbol == ArrayClass - case _ => false - } - } - // At this point it's a module with a main-looking method, so either - // succeed or warn that it isn't. - hasApproximate && { - // Before erasure so we can identify generic mains. - beforeErasure { - val companion = sym.linkedClassOfClass - val companionMain = companion.tpe.member(nme.main) - - if (hasJavaMainMethod(companion)) - failNoForwarder("companion contains its own main method") - else if (companion.tpe.member(nme.main) != NoSymbol) - // this is only because forwarders aren't smart enough yet - failNoForwarder("companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)") - else if (companion.isTrait) - failNoForwarder("companion is a trait") - // Now either succeeed, or issue some additional warnings for things which look like - // attempts to be java main methods. - else possibles exists { m => - m.info match { - case PolyType(_, _) => - fail("main methods cannot be generic.") - case MethodType(params, res) => - if (res.typeSymbol :: params exists (_.isAbstractType)) - fail("main methods cannot refer to type parameters or abstract types.", m.pos) - else - isJavaMainMethod(m) || fail("main method must have exact signature (Array[String])Unit", m.pos) - case tp => - fail("don't know what this is: " + tp, m.pos) - } - } - } - } - } - override def run() { // we reinstantiate the bytecode generator at each run, to allow the GC // to collect everything @@ -210,12 +139,6 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with val BeanDisplayNameAttr = rootMirror.getRequiredClass("scala.beans.BeanDisplayName") val BeanDescriptionAttr = rootMirror.getRequiredClass("scala.beans.BeanDescription") - final val ExcludedForwarderFlags = { - import Flags._ - // Should include DEFERRED but this breaks findMember. - ( CASE | SPECIALIZED | LIFTED | PROTECTED | STATIC | EXPANDEDNAME | BridgeAndPrivateFlags ) - } - // Additional interface parents based on annotations and other cues def newParentForAttr(attr: Symbol): Option[Symbol] = attr match { case SerializableAttr => Some(SerializableClass) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVMASM.scala new file mode 100644 index 0000000000..49c0fa2757 --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVMASM.scala @@ -0,0 +1,97 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Jason Zaugg + */ + +package scala.tools.nsc +package backend.jvm +import scala.tools.nsc.io.AbstractFile +import scala.tools.nsc.symtab._ + +/** Code shared between the legagy backend [[scala.tools.nsc.backend.jvm.GenJVM]] + * and the new backend [[scala.tools.nsc.backend.jvm.GenASM]]. There should be + * more here, but for now I'm starting with the refactorings that are either + * straightforward to review or necessary for maintenance. + */ +trait GenJVMASM { + val global: Global + import global._ + import icodes._ + import definitions._ + + protected def outputDirectory(sym: Symbol): AbstractFile = + settings.outputDirs outputDirFor beforeFlatten(sym.sourceFile) + + protected def getFile(base: AbstractFile, clsName: String, suffix: String): AbstractFile = { + var dir = base + val pathParts = clsName.split("[./]").toList + for (part <- pathParts.init) { + dir = dir.subdirectoryNamed(part) + } + dir.fileNamed(pathParts.last + suffix) + } + protected def getFile(sym: Symbol, clsName: String, suffix: String): AbstractFile = + getFile(outputDirectory(sym), clsName, suffix) + + protected val ExcludedForwarderFlags = { + import Flags._ + // Should include DEFERRED but this breaks findMember. + ( CASE | SPECIALIZED | LIFTED | PROTECTED | STATIC | EXPANDEDNAME | BridgeAndPrivateFlags | MACRO ) + } + + protected def isJavaEntryPoint(icls: IClass) = { + val sym = icls.symbol + def fail(msg: String, pos: Position = sym.pos) = { + icls.cunit.warning(sym.pos, + sym.name + " has a main method with parameter type Array[String], but " + sym.fullName('.') + " will not be a runnable program.\n" + + " Reason: " + msg + // TODO: make this next claim true, if possible + // by generating valid main methods as static in module classes + // not sure what the jvm allows here + // + " You can still run the program by calling it as " + sym.javaSimpleName + " instead." + ) + false + } + def failNoForwarder(msg: String) = { + fail(msg + ", which means no static forwarder can be generated.\n") + } + val possibles = if (sym.hasModuleFlag) (sym.tpe nonPrivateMember nme.main).alternatives else Nil + val hasApproximate = possibles exists { m => + m.info match { + case MethodType(p :: Nil, _) => p.tpe.typeSymbol == ArrayClass + case _ => false + } + } + // At this point it's a module with a main-looking method, so either succeed or warn that it isn't. + hasApproximate && { + // Before erasure so we can identify generic mains. + beforeErasure { + val companion = sym.linkedClassOfClass + val companionMain = companion.tpe.member(nme.main) + + if (hasJavaMainMethod(companion)) + failNoForwarder("companion contains its own main method") + else if (companion.tpe.member(nme.main) != NoSymbol) + // this is only because forwarders aren't smart enough yet + failNoForwarder("companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)") + else if (companion.isTrait) + failNoForwarder("companion is a trait") + // Now either succeeed, or issue some additional warnings for things which look like + // attempts to be java main methods. + else possibles exists { m => + m.info match { + case PolyType(_, _) => + fail("main methods cannot be generic.") + case MethodType(params, res) => + if (res.typeSymbol :: params exists (_.isAbstractType)) + fail("main methods cannot refer to type parameters or abstract types.", m.pos) + else + isJavaMainMethod(m) || fail("main method must have exact signature (Array[String])Unit", m.pos) + case tp => + fail("don't know what this is: " + tp, m.pos) + } + } + } + } + } +} diff --git a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala index bb14c3dce0..23f932b5b4 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala @@ -147,18 +147,18 @@ abstract class ClosureElimination extends SubComponent { case _ => } - case UNBOX(_) => + case UNBOX(boxType) => info.stack match { case Deref(LocalVar(loc1)) :: _ if info.bindings isDefinedAt LocalVar(loc1) => val value = info.getBinding(loc1) value match { - case Boxed(LocalVar(loc2)) => + case Boxed(LocalVar(loc2)) if loc2.kind == boxType => bb.replaceInstruction(i, DROP(icodes.ObjectReference) :: valueToInstruction(info.getBinding(loc2)) :: Nil) debuglog("replaced " + i + " with " + info.getBinding(loc2)) case _ => () } - case Boxed(LocalVar(loc1)) :: _ => + case Boxed(LocalVar(loc1)) :: _ if loc1.kind == boxType => val loc2 = info.getAlias(loc1) bb.replaceInstruction(i, DROP(icodes.ObjectReference) :: valueToInstruction(Deref(LocalVar(loc2))) :: Nil) debuglog("replaced " + i + " with " + LocalVar(loc2)) diff --git a/src/compiler/scala/tools/nsc/doc/DocFactory.scala b/src/compiler/scala/tools/nsc/doc/DocFactory.scala index 642e330a57..a091b04993 100644 --- a/src/compiler/scala/tools/nsc/doc/DocFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/DocFactory.scala @@ -80,7 +80,7 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor with model.ModelFactoryImplicitSupport with model.ModelFactoryTypeSupport with model.diagram.DiagramFactory - with model.comment.CommentFactory + with model.CommentFactory with model.TreeFactory with model.MemberLookup { override def templateShouldDocument(sym: compiler.Symbol, inTpl: DocTemplateImpl) = diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala b/src/compiler/scala/tools/nsc/doc/base/CommentFactoryBase.scala index 40057bbb52..f60d56d9bb 100644..100755 --- a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/base/CommentFactoryBase.scala @@ -1,13 +1,13 @@ /* NSC -- new Scala compiler - * Copyright 2007-2013 LAMP/EPFL + * Copyright 2007-2012 LAMP/EPFL * @author Manohar Jonnalagedda */ package scala.tools.nsc package doc -package model -package comment +package base +import base.comment._ import reporters.Reporter import scala.collection._ import scala.util.matching.Regex @@ -23,78 +23,10 @@ import scala.language.postfixOps * * @author Manohar Jonnalagedda * @author Gilles Dubochet */ -trait CommentFactory { thisFactory: ModelFactory with CommentFactory with MemberLookup=> +trait CommentFactoryBase { this: MemberLookupBase => val global: Global - import global.{ reporter, definitions } - - 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, None) - sym - } - - 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, currentTpl, inTpl) - if (c isDefined) commentCache += (sym, inTpl) -> c.get - c - } - } - - /** A comment is usualy created by the parser, however for some special - * 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, currentTpl: Option[DocTemplateImpl], inTpl: DocTemplateImpl):Option[Comment] = { - - //param accessor case - // We just need the @param argument, we put it into the body - if( sym.isParamAccessor && - inTpl.comment.isDefined && - inTpl.comment.get.valueParams.isDefinedAt(sym.encodedName)) { - val comContent = Some(inTpl.comment.get.valueParams(sym.encodedName)) - Some(createComment(body0 = comContent)) - } - - // Primary constructor case - // We need some content of the class definition : @constructor for the body, - // @param and @deprecated, we can add some more if necessary - else if (sym.isPrimaryConstructor && inTpl.comment.isDefined ) { - val tplComment = inTpl.comment.get - // If there is nothing to put into the comment there is no need to create it - if(tplComment.constructor.isDefined || - tplComment.throws != Map.empty || - tplComment.valueParams != Map.empty || - tplComment.typeParams != Map.empty || - tplComment.deprecated.isDefined - ) - Some(createComment( body0 = tplComment.constructor, - throws0 = tplComment.throws, - valueParams0 = tplComment.valueParams, - typeParams0 = tplComment.typeParams, - deprecated0 = tplComment.deprecated - )) - else None - } - - //other comment cases - // parse function will make the comment - else { - val rawComment = global.expandedDocComment(sym, inTpl.sym).trim - if (rawComment != "") { - val tplOpt = if (currentTpl.isDefined) currentTpl else Some(inTpl) - val c = parse(rawComment, global.rawDocComment(sym), global.docCommentPos(sym), tplOpt) - Some(c) - } - else None - } - - } + import global.{ reporter, definitions, Symbol } /* Creates comments with necessary arguments */ def createComment ( @@ -259,9 +191,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member * @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, inTplOpt: Option[DocTemplateImpl] = None): Comment = { - assert(!inTplOpt.isDefined || inTplOpt.get != null) - + protected def parseAtSymbol(comment: String, src: String, pos: Position, siteOpt: Option[Symbol] = None): Comment = { /** The cleaned raw comment as a list of lines. Cleaning removes comment * start and end markers, line start markers and unnecessary whitespace. */ def clean(comment: String): List[String] = { @@ -387,7 +317,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member 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, inTplOpt))} toSeq: _*) + mutable.Map(tagsWithoutDiagram mapValues {tag => tag map (parseWikiAtSymbol(_, pos, siteOpt))} toSeq: _*) def oneTag(key: SimpleTagKey): Option[Body] = ((bodyTags remove key): @unchecked) match { @@ -420,7 +350,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member } val com = createComment ( - body0 = Some(parseWiki(docBody.toString, pos, inTplOpt)), + body0 = Some(parseWikiAtSymbol(docBody.toString, pos, siteOpt)), authors0 = allTags(SimpleTagKey("author")), see0 = allTags(SimpleTagKey("see")), result0 = oneTag(SimpleTagKey("return")), @@ -460,24 +390,17 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member * - 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, inTplOpt: Option[DocTemplateImpl]): Body = { - assert(!inTplOpt.isDefined || inTplOpt.get != null) - - new WikiParser(string, pos, inTplOpt).document() - } + def parseWikiAtSymbol(string: String, pos: Position, siteOpt: Option[Symbol]): Body = new WikiParser(string, pos, siteOpt).document() /** TODO * * @author Ingo Maier * @author Manohar Jonnalagedda * @author Gilles Dubochet */ - protected final class WikiParser(val buffer: String, pos: Position, inTplOpt: Option[DocTemplateImpl]) extends CharReader(buffer) { wiki => - assert(!inTplOpt.isDefined || inTplOpt.get != null) - + protected final class WikiParser(val buffer: String, pos: Position, siteOpt: Option[Symbol]) extends CharReader(buffer) { wiki => var summaryParsed = false def document(): Body = { - nextChar() val blocks = new mutable.ListBuffer[Block] while (char != endOfText) blocks += block() @@ -559,21 +482,21 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member def code(): Block = { jumpWhitespace() jump("{{{") - readUntil("}}}") + val str = readUntil("}}}") if (char == endOfText) reportError(pos, "unclosed code block") else jump("}}}") blockEnded("code block") - Code(normalizeIndentation(getRead)) + Code(normalizeIndentation(str)) } /** {{{ title ::= ('=' inline '=' | "==" inline "==" | ...) '\n' }}} */ def title(): Block = { jumpWhitespace() - val inLevel = repeatJump("=") + val inLevel = repeatJump('=') val text = inline(check("=" * inLevel)) - val outLevel = repeatJump("=", inLevel) + val outLevel = repeatJump('=', inLevel) if (inLevel != outLevel) reportError(pos, "unbalanced or unclosed heading") blockEnded("heading") @@ -583,7 +506,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member /** {{{ hrule ::= "----" { '-' } '\n' }}} */ def hrule(): Block = { jumpWhitespace() - repeatJump("-") + repeatJump('-') blockEnded("horizontal rule") HorizontalRule() } @@ -621,8 +544,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member } do { - readUntil { char == safeTagMarker || char == endOfText } - val str = getRead() + val str = readUntil { char == safeTagMarker || char == endOfText } nextChar() list += str @@ -660,8 +582,8 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member else if (check(",,")) subscript() else if (check("[[")) link() else { - readUntil { char == safeTagMarker || check("''") || char == '`' || check("__") || char == '^' || check(",,") || check("[[") || isInlineEnd || checkParaEnded || char == endOfLine } - Text(getRead()) + val str = readUntil { char == safeTagMarker || check("''") || char == '`' || check("__") || char == '^' || check(",,") || check("[[") || isInlineEnd || checkParaEnded || char == endOfLine } + Text(str) } } @@ -698,9 +620,8 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member def htmlTag(): HtmlTag = { jump(safeTagMarker) - readUntil(safeTagMarker) + val read = readUntil(safeTagMarker) if (char != endOfText) jump(safeTagMarker) - var read = getRead HtmlTag(read) } @@ -762,14 +683,11 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member def link(): Inline = { val SchemeUri = """([a-z]+:.*)""".r jump("[[") - var parens = 1 - readUntil { parens += 1; !check("[") } - getRead // clear the buffer + var parens = 2 + repeatJump('[') val start = "[" * parens val stop = "]" * parens //println("link with " + parens + " matching parens") - readUntil { check(stop) || check(" ") } - val target = getRead() + val target = readUntil { check(stop) || check(" ") } val title = if (!check(stop)) Some({ jump(" ") @@ -782,7 +700,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member case (SchemeUri(uri), optTitle) => Link(uri, optTitle getOrElse Text(uri)) case (qualName, optTitle) => - makeEntityLink(optTitle getOrElse Text(target), pos, target, inTplOpt) + makeEntityLink(optTitle getOrElse Text(target), pos, target, siteOpt) } } @@ -860,7 +778,6 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member (char == endOfText) || ((char == endOfLine) && { val poff = offset - val pc = char nextChar() // read EOL val ok = { checkSkipInitWhitespace(endOfLine) || @@ -870,7 +787,6 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member checkSkipInitWhitespace('\u003D') } offset = poff - char = pc ok }) } @@ -882,40 +798,31 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member protected sealed class CharReader(buffer: String) { reader => - var char: Char = _ var offset: Int = 0 + def char: Char = + if (offset >= buffer.length) endOfText else buffer charAt offset final def nextChar() { - if (offset >= buffer.length) - char = endOfText - else { - char = buffer charAt offset - offset += 1 - } + offset += 1 } final def check(chars: String): Boolean = { val poff = offset - val pc = char val ok = jump(chars) offset = poff - char = pc ok } def checkSkipInitWhitespace(c: Char): Boolean = { val poff = offset - val pc = char jumpWhitespace() val ok = jump(c) offset = poff - char = pc ok } def checkSkipInitWhitespace(chars: String): Boolean = { val poff = offset - val pc = char jumpWhitespace() val (ok0, chars0) = if (chars.charAt(0) == ' ') @@ -924,20 +831,17 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member (true, chars) val ok = ok0 && jump(chars0) offset = poff - char = pc ok } def countWhitespace: Int = { var count = 0 val poff = offset - val pc = char while (isWhitespace(char) && char != endOfText) { nextChar() count += 1 } offset = poff - char = pc count } @@ -964,38 +868,10 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member index == chars.length } - final def checkedJump(chars: String): Boolean = { - val poff = offset - val pc = char - val ok = jump(chars) - if (!ok) { - offset = poff - char = pc - } - ok - } - - final def repeatJump(chars: String, max: Int): Int = { - var count = 0 - var more = true - while (more && count < max) { - if (!checkedJump(chars)) - more = false - else - count += 1 - } - count - } - - final def repeatJump(chars: String): Int = { + final def repeatJump(c: Char, max: Int = Int.MaxValue): Int = { var count = 0 - var more = true - while (more) { - if (!checkedJump(chars)) - more = false - else - count += 1 - } + while (jump(c) && count < max) + count += 1 count } @@ -1035,47 +911,41 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory with Member /* READERS */ - private val readBuilder = new mutable.StringBuilder - - final def getRead(): String = { - val bld = readBuilder.toString - readBuilder.clear() - if (bld.length < 6) bld.intern else bld - } - - final def readUntil(ch: Char): Int = { - var count = 0 - while (char != ch && char != endOfText) { - readBuilder += char - nextChar() + final def readUntil(c: Char): String = { + withRead { + while (char != c && char != endOfText) { + nextChar() + } } - count } - final def readUntil(chars: String): Int = { + final def readUntil(chars: String): String = { assert(chars.length > 0) - var count = 0 - val c = chars.charAt(0) - while (!check(chars) && char != endOfText) { - readBuilder += char - nextChar() - while (char != c && char != endOfText) { - readBuilder += char + withRead { + val c = chars.charAt(0) + while (!check(chars) && char != endOfText) { nextChar() + while (char != c && char != endOfText) + nextChar() } } - count } - final def readUntil(pred: => Boolean): Int = { - var count = 0 - while (!pred && char != endOfText) { - readBuilder += char - nextChar() + final def readUntil(pred: => Boolean): String = { + withRead { + while (char != endOfText && !pred) { + nextChar() + } } - count } + private def withRead(read: => Unit): String = { + val start = offset + read + buffer.substring(start, offset) + } + + /* CHARS CLASSES */ def isWhitespace(c: Char) = c == ' ' || c == '\t' diff --git a/src/compiler/scala/tools/nsc/doc/base/LinkTo.scala b/src/compiler/scala/tools/nsc/doc/base/LinkTo.scala new file mode 100755 index 0000000000..c11179800c --- /dev/null +++ b/src/compiler/scala/tools/nsc/doc/base/LinkTo.scala @@ -0,0 +1,15 @@ +/* NSC -- new Scala compiler + * Copyright 2007-2013 LAMP/EPFL + */ + +package scala.tools.nsc +package doc +package base + +import scala.collection._ + +sealed trait LinkTo +final case class LinkToMember[Mbr, Tpl](mbr: Mbr, tpl: Tpl) extends LinkTo +final case class LinkToTpl[Tpl](tpl: Tpl) extends LinkTo +final case class LinkToExternal(name: String, url: String) extends LinkTo +final case class Tooltip(name: String) extends LinkTo diff --git a/src/compiler/scala/tools/nsc/doc/base/MemberLookupBase.scala b/src/compiler/scala/tools/nsc/doc/base/MemberLookupBase.scala new file mode 100755 index 0000000000..35390adcd9 --- /dev/null +++ b/src/compiler/scala/tools/nsc/doc/base/MemberLookupBase.scala @@ -0,0 +1,229 @@ +package scala.tools.nsc +package doc +package base + +import comment._ + +/** This trait extracts all required information for documentation from compilation units. + * The base trait has been extracted to allow getting light-weight documentation + * for a particular symbol in the IDE.*/ +trait MemberLookupBase { + + val global: Global + val settings: doc.Settings + + import global._ + def internalLink(sym: Symbol, site: Symbol): Option[LinkTo] + def chooseLink(links: List[LinkTo]): LinkTo + def toString(link: LinkTo): String + + import global._ + import definitions.{ NothingClass, AnyClass, AnyValClass, AnyRefClass, ListClass } + import rootMirror.{RootPackage, EmptyPackage} + + private def isRoot(s: Symbol) = s.isRootSymbol || s.isEmptyPackage || s.isEmptyPackageClass + + def makeEntityLink(title: Inline, pos: Position, query: String, siteOpt: Option[Symbol]) = + new EntityLink(title) { lazy val link = memberLookup(pos, query, siteOpt) } + + private var showExplanation = true + private def explanation: String = + if (showExplanation) { + showExplanation = false + """ + |Quick crash course on using Scaladoc links + |========================================== + |Disambiguating terms and types: Prefix terms with '$' and types with '!' in case both names are in use: + | - [[scala.collection.immutable.List!.apply class List's apply method]] and + | - [[scala.collection.immutable.List$.apply object List's apply method]] + |Disambiguating overloaded members: If a term is overloaded, you can indicate the first part of its signature followed by *: + | - [[[scala.collection.immutable.List$.fill[A](Int)(⇒A):List[A]* Fill with a single parameter]]] + | - [[[scala.collection.immutable.List$.fill[A](Int,Int)(⇒A):List[List[A]]* Fill with a two parameters]]] + |Notes: + | - you can use any number of matching square brackets to avoid interference with the signature + | - you can use \\. to escape dots in prefixes (don't forget to use * at the end to match the signature!) + | - you can use \\# to escape hashes, otherwise they will be considered as delimiters, like dots.""".stripMargin + } else "" + + def memberLookup(pos: Position, query: String, siteOpt: Option[Symbol]): LinkTo = { + var members = breakMembers(query) + + // (1) First look in the root package, as most of the links are qualified + val fromRoot = lookupInRootPackage(pos, members) + + // (2) Or recursively go into each containing template. + val fromParents = siteOpt.fold(Stream.empty[Symbol]) { s => + Stream.iterate(s)(_.owner) + }.takeWhile (!isRoot(_)).map { + lookupInTemplate(pos, members, _) + } + + val syms = (fromRoot +: fromParents) find (!_.isEmpty) getOrElse Nil + + val links = syms flatMap { case (sym, site) => internalLink(sym, site) } match { + case Nil => + // (3) Look at external links + syms.flatMap { case (sym, owner) => + // reconstruct the original link + def linkName(sym: Symbol) = { + def nameString(s: Symbol) = s.nameString + (if ((s.isModule || s.isModuleClass) && !s.isPackage) "$" else "") + val packageSuffix = if (sym.isPackage) ".package" else "" + + sym.ownerChain.reverse.filterNot(isRoot(_)).map(nameString(_)).mkString(".") + packageSuffix + } + + if (sym.isClass || sym.isModule || sym.isTrait || sym.isPackage) + findExternalLink(sym, linkName(sym)) + else if (owner.isClass || owner.isModule || owner.isTrait || owner.isPackage) + findExternalLink(sym, linkName(owner) + "@" + externalSignature(sym)) + else + None + } + case links => links + } + links match { + case Nil => + if (!settings.docNoLinkWarnings.value) + reporter.warning(pos, "Could not find any member to link for \"" + query + "\".") + // (4) if we still haven't found anything, create a tooltip + Tooltip(query) + case List(l) => l + case links => + val chosen = chooseLink(links) + def linkToString(link: LinkTo) = { + val chosenInfo = + if (link == chosen) " [chosen]" else "" + toString(link) + chosenInfo + "\n" + } + if (!settings.docNoLinkWarnings.value) { + val allLinks = links.map(linkToString).mkString + reporter.warning(pos, + s"""The link target \"$query\" is ambiguous. Several members fit the target: + |$allLinks + |$explanation""".stripMargin) + } + chosen + } + } + + private sealed trait SearchStrategy + private case object BothTypeAndTerm extends SearchStrategy + private case object OnlyType extends SearchStrategy + private case object OnlyTerm extends SearchStrategy + + private def lookupInRootPackage(pos: Position, members: List[String]) = + lookupInTemplate(pos, members, EmptyPackage) ::: lookupInTemplate(pos, members, RootPackage) + + private def lookupInTemplate(pos: Position, members: List[String], container: Symbol): List[(Symbol, Symbol)] = { + // 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 syms = lookupInTemplate(pos, mbrName, container, OnlyType) map ((_, container)) + if (syms.isEmpty) + syms = lookupInTemplate(pos, mbrName, container, OnlyTerm) map ((_, container)) + syms + + case tplName::rest => + def completeSearch(syms: List[Symbol]) = + syms flatMap (lookupInTemplate(pos, rest, _)) + + completeSearch(lookupInTemplate(pos, tplName, container, OnlyTerm)) match { + case Nil => completeSearch(lookupInTemplate(pos, tplName, container, OnlyType)) + case syms => syms + } + } + //println("lookupInTemplate(" + members + ", " + container + ") => " + result) + result + } + + private def lookupInTemplate(pos: Position, member: String, container: Symbol, strategy: SearchStrategy): List[Symbol] = { + val name = member.stripSuffix("$").stripSuffix("!").stripSuffix("*") + def signatureMatch(sym: Symbol): Boolean = externalSignature(sym).startsWith(name) + + // We need to cleanup the bogus classes created by the .class file parser. For example, [[scala.Predef]] resolves + // to (bogus) class scala.Predef loaded by the class loader -- which we need to eliminate by looking at the info + // and removing NoType classes + def cleanupBogusClasses(syms: List[Symbol]) = { syms.filter(_.info != NoType) } + + def syms(name: Name) = container.info.nonPrivateMember(name.encodedName).alternatives + def termSyms = cleanupBogusClasses(syms(newTermName(name))) + def typeSyms = cleanupBogusClasses(syms(newTypeName(name))) + + val result = if (member.endsWith("$")) + termSyms + else if (member.endsWith("!")) + typeSyms + else if (member.endsWith("*")) + cleanupBogusClasses(container.info.nonPrivateDecls) filter signatureMatch + else + strategy match { + case BothTypeAndTerm => termSyms ::: typeSyms + case OnlyType => typeSyms + case OnlyTerm => termSyms + } + + //println("lookupInTemplate(" + member + ", " + container + ") => " + 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) != '\\'))) { + + val member = query.substring(last_index, index).replaceAll("\\\\([#\\.])", "$1") + // we want to allow javadoc-style links [[#member]] -- which requires us to remove empty members from the first + // elemnt in the list + if ((member != "") || (!members.isEmpty)) + members ::= member + last_index = index + 1 + } + index += 1 + } + if (last_index < length) + members ::= query.substring(last_index, length).replaceAll("\\\\\\.", ".") + members.reverse + } + + + def findExternalLink(sym: Symbol, name: String): Option[LinkToExternal] = { + val sym1 = + if (sym == AnyClass || sym == AnyRefClass || sym == AnyValClass || sym == NothingClass) ListClass + else if (sym.isPackage) + /* Get package object which has associatedFile ne null */ + sym.info.member(newTermName("package")) + else sym + Option(sym1.associatedFile) flatMap (_.underlyingSource) flatMap { src => + val path = src.path + settings.extUrlMapping get path map { url => + LinkToExternal(name, url + "#" + name) + } + } orElse { + // Deprecated option. + settings.extUrlPackageMapping find { + case (pkg, _) => name startsWith pkg + } map { + case (_, url) => LinkToExternal(name, url + "#" + name) + } + } + } + + def externalSignature(sym: Symbol) = { + sym.info // force it, otherwise we see lazy types + (sym.nameString + sym.signatureString).replaceAll("\\s", "") + } +} diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala b/src/compiler/scala/tools/nsc/doc/base/comment/Body.scala index 3e5e634e18..02e662da85 100644..100755 --- a/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala +++ b/src/compiler/scala/tools/nsc/doc/base/comment/Body.scala @@ -5,7 +5,7 @@ package scala.tools.nsc package doc -package model +package base package comment import scala.collection._ diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala b/src/compiler/scala/tools/nsc/doc/base/comment/Comment.scala index 3e172544dd..2b28164ca4 100644 --- a/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala +++ b/src/compiler/scala/tools/nsc/doc/base/comment/Comment.scala @@ -5,7 +5,7 @@ package scala.tools.nsc package doc -package model +package base package comment import scala.collection._ @@ -131,5 +131,4 @@ abstract class Comment { (authors map ("@author " + _.toString)).mkString("\n") + (result map ("@return " + _.toString)).mkString("\n") + (version map ("@version " + _.toString)).mkString - } diff --git a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala index e8131e242b..69da322418 100644 --- a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala +++ b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala @@ -7,8 +7,9 @@ package scala.tools.nsc package doc package html +import base._ +import base.comment._ import model._ -import comment._ import scala.xml.{XML, NodeSeq} import scala.xml.dtd.{DocType, PublicID} @@ -126,12 +127,12 @@ abstract class HtmlPage extends Page { thisPage => } def linkToHtml(text: Inline, link: LinkTo, hasLinks: Boolean) = link match { - case LinkToTpl(dtpl) => + case LinkToTpl(dtpl: TemplateEntity) => 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) => + case LinkToMember(mbr: MemberEntity, inTpl: TemplateEntity) => if (hasLinks) <a href={ relativeLinkTo(inTpl) + "#" + mbr.signature } class="extmbr" name={ mbr.qualifiedName }>{ inlineToHtml(text) }</a> else @@ -140,7 +141,7 @@ abstract class HtmlPage extends Page { thisPage => <span class="extype" name={ tooltip }>{ inlineToHtml(text) }</span> case LinkToExternal(name, url) => <a href={ url } class="extype" target="_top">{ inlineToHtml(text) }</a> - case NoLink => + case _ => inlineToHtml(text) } diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Source.scala b/src/compiler/scala/tools/nsc/doc/html/page/Source.scala index 807a1bc11a..68289b7474 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Source.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Source.scala @@ -9,7 +9,6 @@ package html package page import model._ -import comment._ import scala.xml.{NodeSeq, Unparsed} import java.io.File 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 008999e09f..ea07ff29c4 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala @@ -8,6 +8,9 @@ package doc package html package page +import base._ +import base.comment._ + import model._ import model.diagram._ import diagram._ @@ -332,12 +335,10 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp } } - def memberToShortCommentHtml(mbr: MemberEntity, isSelf: Boolean): NodeSeq = { - if (mbr.comment.isEmpty) - NodeSeq.Empty - else - <p class="shortcomment cmt">{ memberToUseCaseCommentHtml(mbr, isSelf) }{ inlineToHtml(mbr.comment.get.short) }</p> - } + def memberToShortCommentHtml(mbr: MemberEntity, isSelf: Boolean): NodeSeq = + mbr.comment.fold(NodeSeq.Empty) { comment => + <p class="shortcomment cmt">{ memberToUseCaseCommentHtml(mbr, isSelf) }{ inlineToHtml(comment.short) }</p> + } def memberToInlineCommentHtml(mbr: MemberEntity, isSelf: Boolean): NodeSeq = <p class="comment cmt">{ inlineToHtml(mbr.comment.get.short) }</p> @@ -358,37 +359,34 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp case _ => Nil } - def mbrCmt = mbr.comment.get - - def paramCommentToHtml(prs: List[ParameterEntity]): NodeSeq = prs match { + def paramCommentToHtml(prs: List[ParameterEntity], comment: Comment): NodeSeq = prs match { case (tp: TypeParam) :: rest => val paramEntry: NodeSeq = { - <dt class="tparam">{ tp.name }</dt><dd class="cmt">{ bodyToHtml(mbrCmt.typeParams(tp.name)) }</dd> + <dt class="tparam">{ tp.name }</dt><dd class="cmt">{ bodyToHtml(comment.typeParams(tp.name)) }</dd> } - paramEntry ++ paramCommentToHtml(rest) + paramEntry ++ paramCommentToHtml(rest, comment) case (vp: ValueParam) :: rest => val paramEntry: NodeSeq = { - <dt class="param">{ vp.name }</dt><dd class="cmt">{ bodyToHtml(mbrCmt.valueParams(vp.name)) }</dd> + <dt class="param">{ vp.name }</dt><dd class="cmt">{ bodyToHtml(comment.valueParams(vp.name)) }</dd> } - paramEntry ++ paramCommentToHtml(rest) + paramEntry ++ paramCommentToHtml(rest, comment) case _ => NodeSeq.Empty } - if (mbr.comment.isEmpty) NodeSeq.Empty - else { + mbr.comment.fold(NodeSeq.Empty) { comment => val cmtedPrs = prs filter { - case tp: TypeParam => mbrCmt.typeParams isDefinedAt tp.name - case vp: ValueParam => mbrCmt.valueParams isDefinedAt vp.name + case tp: TypeParam => comment.typeParams isDefinedAt tp.name + case vp: ValueParam => comment.valueParams isDefinedAt vp.name } - if (cmtedPrs.isEmpty && mbrCmt.result.isEmpty) NodeSeq.Empty + if (cmtedPrs.isEmpty && comment.result.isEmpty) NodeSeq.Empty else { <dl class="paramcmts block">{ - paramCommentToHtml(cmtedPrs) ++ ( - mbrCmt.result match { + paramCommentToHtml(cmtedPrs, comment) ++ ( + comment.result match { case None => NodeSeq.Empty case Some(cmt) => <dt>returns</dt><dd class="cmt">{ bodyToHtml(cmt) }</dd> @@ -467,7 +465,7 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp } // --- start attributes block vals - val attributes: Seq[scala.xml.Node] = { + val attributes: NodeSeq = { val fvs: List[comment.Paragraph] = visibility(mbr).toList if (fvs.isEmpty || isReduced) NodeSeq.Empty else { @@ -476,7 +474,7 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp } } - val definitionClasses: Seq[scala.xml.Node] = { + val definitionClasses: NodeSeq = { val inDefTpls = mbr.inDefinitionTemplates if ((inDefTpls.tail.isEmpty && (inDefTpls.head == inTpl)) || isReduced) NodeSeq.Empty else { @@ -485,7 +483,7 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp } } - val fullSignature: Seq[scala.xml.Node] = { + val fullSignature: NodeSeq = { mbr match { case nte: NonTemplateMemberEntity if nte.isUseCase => <div class="full-signature-block toggleContainer"> @@ -496,14 +494,14 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp } } - val selfType: Seq[scala.xml.Node] = mbr match { + val selfType: NodeSeq = mbr match { case dtpl: DocTemplateEntity if (isSelf && !dtpl.selfType.isEmpty && !isReduced) => <dt>Self Type</dt> <dd>{ typeToHtml(dtpl.selfType.get, hasLinks = true) }</dd> case _ => NodeSeq.Empty } - val annotations: Seq[scala.xml.Node] = { + val annotations: NodeSeq = { // A list of annotations which don't show their arguments, e. g. because they are shown separately. val annotationsWithHiddenArguments = List("deprecated", "Deprecated", "migration") @@ -525,7 +523,7 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp } else NodeSeq.Empty } - val sourceLink: Seq[scala.xml.Node] = mbr match { + val sourceLink: NodeSeq = mbr match { case dtpl: DocTemplateEntity if (isSelf && dtpl.sourceUrl.isDefined && dtpl.inSource.isDefined && !isReduced) => val (absFile, line) = dtpl.inSource.get <dt>Source</dt> @@ -533,83 +531,87 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp case _ => NodeSeq.Empty } - val deprecation: Seq[scala.xml.Node] = - if (mbr.deprecation.isEmpty || isReduced) NodeSeq.Empty - else { - <dt>Deprecated</dt> - <dd class="cmt">{ bodyToHtml(mbr.deprecation.get) }</dd> + val deprecation: NodeSeq = + mbr.deprecation match { + case Some(deprecation) if !isReduced => + <dt>Deprecated</dt> + <dd class="cmt">{ bodyToHtml(deprecation) }</dd> + case _ => NodeSeq.Empty } - val migration: Seq[scala.xml.Node] = - if(mbr.migration.isEmpty || isReduced) NodeSeq.Empty - else { + val migration: NodeSeq = + mbr.migration match { + case Some(migration) if !isReduced => <dt>Migration</dt> - <dd class="cmt">{ bodyToHtml(mbr.migration.get) }</dd> + <dd class="cmt">{ bodyToHtml(migration) }</dd> + case _ => NodeSeq.Empty } - val mainComment: Seq[scala.xml.Node] = mbr.comment match { + val mainComment: NodeSeq = mbr.comment match { case Some(comment) if (! isReduced) => + def orEmpty[T](it: Iterable[T])(gen: =>NodeSeq): NodeSeq = + if (it.isEmpty) NodeSeq.Empty else gen + val example = - if(!comment.example.isEmpty) + orEmpty(comment.example) { <div class="block">Example{ if (comment.example.length > 1) "s" else ""}: - <ol>{ - val exampleXml: List[scala.xml.NodeSeq] = - for(example <- comment.example ) yield - <li class="cmt">{ bodyToHtml(example) }</li> - exampleXml.reduceLeft(_ ++ Text(", ") ++ _) + <ol>{ + val exampleXml: List[NodeSeq] = for (ex <- comment.example) yield + <li class="cmt">{ bodyToHtml(ex) }</li> + exampleXml.reduceLeft(_ ++ Text(", ") ++ _) }</ol> - </div> - else NodeSeq.Empty + </div> + } - val version: Seq[scala.xml.Node] = - if(!comment.version.isEmpty) { + val version: NodeSeq = + orEmpty(comment.version) { <dt>Version</dt> - <dd>{ for(body <- comment.version.toList) yield {bodyToHtml(body)} }</dd> - } else NodeSeq.Empty + <dd>{ for(body <- comment.version.toList) yield bodyToHtml(body) }</dd> + } - val sinceVersion: Seq[scala.xml.Node] = - if(!comment.since.isEmpty) { + val sinceVersion: NodeSeq = + orEmpty(comment.since) { <dt>Since</dt> - <dd>{ for(body <- comment.since.toList) yield {bodyToHtml(body)} }</dd> - } else NodeSeq.Empty + <dd>{ for(body <- comment.since.toList) yield bodyToHtml(body) }</dd> + } - val note: Seq[scala.xml.Node] = - if(!comment.note.isEmpty) { + val note: NodeSeq = + orEmpty(comment.note) { <dt>Note</dt> <dd>{ - val noteXml: List[scala.xml.NodeSeq] = (for(note <- comment.note ) yield <span class="cmt">{bodyToHtml(note)}</span> ) + val noteXml: List[NodeSeq] = for(note <- comment.note ) yield <span class="cmt">{bodyToHtml(note)}</span> noteXml.reduceLeft(_ ++ Text(", ") ++ _) }</dd> - } else NodeSeq.Empty + } - val seeAlso: Seq[scala.xml.Node] = - if(!comment.see.isEmpty) { + val seeAlso: NodeSeq = + orEmpty(comment.see) { <dt>See also</dt> <dd>{ - val seeXml:List[scala.xml.NodeSeq]=(for(see <- comment.see ) yield <span class="cmt">{bodyToHtml(see)}</span> ) + val seeXml: List[NodeSeq] = for(see <- comment.see ) yield <span class="cmt">{bodyToHtml(see)}</span> seeXml.reduceLeft(_ ++ _) }</dd> - } else NodeSeq.Empty + } - val exceptions: Seq[scala.xml.Node] = - if(!comment.throws.isEmpty) { + val exceptions: NodeSeq = + orEmpty(comment.throws) { <dt>Exceptions thrown</dt> <dd>{ - val exceptionsXml: Iterable[scala.xml.NodeSeq] = - for(exception <- comment.throws.toList.sortBy(_._1) ) yield - <span class="cmt">{Text(exception._1) ++ bodyToHtml(exception._2)}</span> + val exceptionsXml: List[NodeSeq] = + for((name, body) <- comment.throws.toList.sortBy(_._1) ) yield + <span class="cmt">{Text(name) ++ bodyToHtml(body)}</span> exceptionsXml.reduceLeft(_ ++ Text("") ++ _) }</dd> - } else NodeSeq.Empty + } - val todo: Seq[scala.xml.Node] = - if(!comment.todo.isEmpty) { + val todo: NodeSeq = + orEmpty(comment.todo) { <dt>To do</dt> <dd>{ - val todoXml: List[scala.xml.NodeSeq] = (for(todo <- comment.todo ) yield <span class="cmt">{bodyToHtml(todo)}</span> ) + val todoXml: List[NodeSeq] = (for(todo <- comment.todo ) yield <span class="cmt">{bodyToHtml(todo)}</span> ) todoXml.reduceLeft(_ ++ Text(", ") ++ _) }</dd> - } else NodeSeq.Empty + } example ++ version ++ sinceVersion ++ exceptions ++ todo ++ note ++ seeAlso 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 304c534bdc..847367838c 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 @@ -74,7 +74,7 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator { def textTypeEntity(text: String) = new TypeEntity { val name = text - def refEntity: SortedMap[Int, (LinkTo, Int)] = SortedMap() + def refEntity: SortedMap[Int, (base.LinkTo, Int)] = SortedMap() } // it seems dot chokes on node names over 8000 chars, so let's limit the size of the string diff --git a/src/compiler/scala/tools/nsc/doc/model/CommentFactory.scala b/src/compiler/scala/tools/nsc/doc/model/CommentFactory.scala new file mode 100644 index 0000000000..9ba89146c0 --- /dev/null +++ b/src/compiler/scala/tools/nsc/doc/model/CommentFactory.scala @@ -0,0 +1,114 @@ +/* NSC -- new Scala compiler + * Copyright 2007-2013 LAMP/EPFL + * @author Manohar Jonnalagedda + */ + +package scala.tools.nsc +package doc +package model + +import base.comment._ + +import reporters.Reporter +import scala.collection._ +import scala.reflect.internal.util.{NoPosition, Position} +import scala.language.postfixOps + +/** The comment parser transforms raw comment strings into `Comment` objects. + * Call `parse` to run the parser. Note that the parser is stateless and + * should only be built once for a given Scaladoc run. + * + * @param reporter The reporter on which user messages (error, warnings) should be printed. + * + * @author Manohar Jonnalagedda + * @author Gilles Dubochet */ +trait CommentFactory extends base.CommentFactoryBase { + thisFactory: ModelFactory with CommentFactory with MemberLookup => + + val global: Global + import global.{ reporter, definitions, Symbol } + + protected val commentCache = mutable.HashMap.empty[(Symbol, TemplateImpl), Comment] + + def addCommentBody(sym: Symbol, inTpl: TemplateImpl, docStr: String, docPos: global.Position): Symbol = { + commentCache += (sym, inTpl) -> parse(docStr, docStr, docPos, None) + sym + } + + def comment(sym: Symbol, currentTpl: Option[DocTemplateImpl], inTpl: DocTemplateImpl): Option[Comment] = { + val key = (sym, inTpl) + if (commentCache isDefinedAt key) + Some(commentCache(key)) + else { + val c = defineComment(sym, currentTpl, inTpl) + if (c isDefined) commentCache += (sym, inTpl) -> c.get + c + } + } + + /** A comment is usualy created by the parser, however for some special + * 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: Symbol, currentTpl: Option[DocTemplateImpl], inTpl: DocTemplateImpl):Option[Comment] = { + + //param accessor case + // We just need the @param argument, we put it into the body + if( sym.isParamAccessor && + inTpl.comment.isDefined && + inTpl.comment.get.valueParams.isDefinedAt(sym.encodedName)) { + val comContent = Some(inTpl.comment.get.valueParams(sym.encodedName)) + Some(createComment(body0 = comContent)) + } + + // Primary constructor case + // We need some content of the class definition : @constructor for the body, + // @param and @deprecated, we can add some more if necessary + else if (sym.isPrimaryConstructor && inTpl.comment.isDefined ) { + val tplComment = inTpl.comment.get + // If there is nothing to put into the comment there is no need to create it + if(tplComment.constructor.isDefined || + tplComment.throws != Map.empty || + tplComment.valueParams != Map.empty || + tplComment.typeParams != Map.empty || + tplComment.deprecated.isDefined + ) + Some(createComment( body0 = tplComment.constructor, + throws0 = tplComment.throws, + valueParams0 = tplComment.valueParams, + typeParams0 = tplComment.typeParams, + deprecated0 = tplComment.deprecated + )) + else None + } + + //other comment cases + // parse function will make the comment + else { + val rawComment = global.expandedDocComment(sym, inTpl.sym).trim + if (rawComment != "") { + val tplOpt = if (currentTpl.isDefined) currentTpl else Some(inTpl) + val c = parse(rawComment, global.rawDocComment(sym), global.docCommentPos(sym), tplOpt) + Some(c) + } + else None + } + + } + + protected def parse(comment: String, src: String, pos: Position, inTplOpt: Option[DocTemplateImpl] = None): Comment = { + assert(!inTplOpt.isDefined || inTplOpt.get != null) + parseAtSymbol(comment, src, pos, inTplOpt map (_.sym)) + } + + /** Parses a string containing wiki syntax into a `Comment` object. + * Note that the string is assumed to be clean: + * - Removed Scaladoc start and end markers. + * - 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, inTplOpt: Option[DocTemplateImpl]): Body = { + assert(!inTplOpt.isDefined || inTplOpt.get != null) + parseWikiAtSymbol(string,pos, inTplOpt map (_.sym)) + } +} diff --git a/src/compiler/scala/tools/nsc/doc/model/Entity.scala b/src/compiler/scala/tools/nsc/doc/model/Entity.scala index c3f9101f17..cbc1a23d44 100644 --- a/src/compiler/scala/tools/nsc/doc/model/Entity.scala +++ b/src/compiler/scala/tools/nsc/doc/model/Entity.scala @@ -9,7 +9,7 @@ package doc package model import scala.collection._ -import comment._ +import base.comment._ import diagram._ /** An entity in a Scaladoc universe. Entities are declarations in the program and correspond to symbols in the diff --git a/src/compiler/scala/tools/nsc/doc/model/LinkTo.scala b/src/compiler/scala/tools/nsc/doc/model/LinkTo.scala deleted file mode 100644 index 6c13d5a6d3..0000000000 --- a/src/compiler/scala/tools/nsc/doc/model/LinkTo.scala +++ /dev/null @@ -1,24 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2007-2013 LAMP/EPFL - */ - -package scala.tools.nsc -package doc -package model - -import scala.collection._ - -abstract sealed class LinkTo -final case class LinkToTpl(tpl: DocTemplateEntity) extends LinkTo -final case class LinkToMember(mbr: MemberEntity, inTpl: DocTemplateEntity) extends LinkTo -final case class Tooltip(name: String) extends LinkTo { def this(tpl: TemplateEntity) = this(tpl.qualifiedName) } -final case class LinkToExternal(name: String, url: String) extends LinkTo -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 index 5257db1610..c7a767f992 100644 --- a/src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala +++ b/src/compiler/scala/tools/nsc/doc/model/MemberLookup.scala @@ -2,227 +2,37 @@ package scala.tools.nsc package doc package model -import comment._ - -import scala.reflect.internal.util.FakePos //Position +import base._ /** This trait extracts all required information for documentation from compilation units */ -trait MemberLookup { +trait MemberLookup extends base.MemberLookupBase { thisFactory: ModelFactory => import global._ - import rootMirror.RootPackage, rootMirror.EmptyPackage - - 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) First look in the root package, as most of the links are qualified - val fromRoot = lookupInRootPackage(pos, members) - - // (2) Or recursively go into each containing template. - val fromParents = inTplOpt.fold(Stream.empty[DocTemplateImpl]) { tpl => - Stream.iterate(tpl)(_.inTemplate) - }.takeWhile (tpl => tpl != null && !tpl.isRootPackage).map { tpl => - lookupInTemplate(pos, members, tpl.asInstanceOf[EntityImpl].sym) - } - - val syms = (fromRoot +: fromParents) find (!_.isEmpty) getOrElse Nil - val linkTo = createLinks(syms) match { - case Nil if !syms.isEmpty => - // (3) Look at external links - syms.flatMap { case (sym, owner) => - - // reconstruct the original link - def linkName(sym: Symbol) = { - def isRoot(s: Symbol) = s.isRootSymbol || s.isEmptyPackage || s.isEmptyPackageClass - def nameString(s: Symbol) = s.nameString + (if ((s.isModule || s.isModuleClass) && !s.isPackage) "$" else "") - val packageSuffix = if (sym.isPackage) ".package" else "" - sym.ownerChain.reverse.filterNot(isRoot(_)).map(nameString(_)).mkString(".") + packageSuffix - } - - if (sym.isClass || sym.isModule || sym.isTrait || sym.isPackage) - findExternalLink(sym, linkName(sym)) - else if (owner.isClass || owner.isModule || owner.isTrait || owner.isPackage) - findExternalLink(sym, linkName(owner) + "@" + externalSignature(sym)) - else - None + override def internalLink(sym: Symbol, site: Symbol): Option[LinkTo] = + findTemplateMaybe(sym) match { + case Some(tpl) => Some(LinkToTpl(tpl)) + case None => + findTemplateMaybe(site) flatMap { inTpl => + inTpl.members find (_.asInstanceOf[EntityImpl].sym == sym) map (LinkToMember(_, inTpl)) } - case links => links } - //println(createLinks(syms)) - //println(linkTo) - - // (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 + override def chooseLink(links: List[LinkTo]): LinkTo = { + val mbrs = links.collect { + case lm@LinkToMember(mbr: MemberEntity, _) => (mbr, lm) } - } - - 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, EmptyPackage) ::: lookupInTemplate(pos, members, RootPackage) - - private def createLinks(syms: List[(Symbol, Symbol)]): List[LinkTo] = - syms.flatMap { case (sym, owner) => - findTemplateMaybe(sym) match { - case Some(tpl) => LinkToTpl(tpl) :: Nil - case None => - findTemplateMaybe(owner) flatMap { inTpl => - inTpl.members find (_.asInstanceOf[EntityImpl].sym == sym) map (LinkToMember(_, inTpl)) - } - } - } - - private def lookupInTemplate(pos: Position, members: List[String], container: Symbol): List[(Symbol, Symbol)] = { - // 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 syms = lookupInTemplate(pos, mbrName, container, OnlyType) map ((_, container)) - if (syms.isEmpty) - syms = lookupInTemplate(pos, mbrName, container, OnlyTerm) map ((_, container)) - syms - - case tplName::rest => - def completeSearch(syms: List[Symbol]) = - syms flatMap (lookupInTemplate(pos, rest, _)) - - completeSearch(lookupInTemplate(pos, tplName, container, OnlyTerm)) match { - case Nil => completeSearch(lookupInTemplate(pos, tplName, container, OnlyType)) - case syms => syms - } - } - //println("lookupInTemplate(" + members + ", " + container + ") => " + result) - result - } - - private def lookupInTemplate(pos: Position, member: String, container: Symbol, strategy: SearchStrategy): List[Symbol] = { - val name = member.stripSuffix("$").stripSuffix("!").stripSuffix("*") - def signatureMatch(sym: Symbol): Boolean = externalSignature(sym).startsWith(name) - - // We need to cleanup the bogus classes created by the .class file parser. For example, [[scala.Predef]] resolves - // to (bogus) class scala.Predef loaded by the class loader -- which we need to eliminate by looking at the info - // and removing NoType classes - def cleanupBogusClasses(syms: List[Symbol]) = { syms.filter(_.info != NoType) } - - def syms(name: Name) = container.info.nonPrivateMember(name.encodedName).alternatives - def termSyms = cleanupBogusClasses(syms(newTermName(name))) - def typeSyms = cleanupBogusClasses(syms(newTypeName(name))) - - val result = if (member.endsWith("$")) - termSyms - else if (member.endsWith("!")) - typeSyms - else if (member.endsWith("*")) - cleanupBogusClasses(container.info.nonPrivateDecls) filter signatureMatch + if (mbrs.isEmpty) + links.head else - if (strategy == BothTypeAndTerm) - termSyms ::: typeSyms - else if (strategy == OnlyType) - typeSyms - else if (strategy == OnlyTerm) - termSyms - else - Nil - - //println("lookupInTemplate(" + member + ", " + container + ") => " + result) - result + mbrs.min(Ordering[MemberEntity].on[(MemberEntity, LinkTo)](_._1))._2 } - 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) != '\\'))) { - - val member = query.substring(last_index, index).replaceAll("\\\\([#\\.])", "$1") - // we want to allow javadoc-style links [[#member]] -- which requires us to remove empty members from the first - // elemnt in the list - if ((member != "") || (!members.isEmpty)) - members ::= member - last_index = index + 1 - } - index += 1 - } - if (last_index < length) - members ::= query.substring(last_index, length).replaceAll("\\\\\\.", ".") - members.reverse + override def toString(link: LinkTo) = link match { + case LinkToTpl(tpl: EntityImpl) => tpl.sym.toString + case LinkToMember(mbr: EntityImpl, inTpl: EntityImpl) => + mbr.sym.signatureString + " in " + inTpl.sym.toString + case _ => link.toString } } - -object MemberLookup { - private[this] var _showExplanation = true - def showExplanation: Boolean = if (_showExplanation) { _showExplanation = false; true } else false -} diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala index 3ae1210ebf..c6cfc317ea 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala @@ -4,8 +4,8 @@ package scala.tools.nsc package doc package model -import comment._ - +import base._ +import base.comment._ import diagram._ import scala.collection._ @@ -950,7 +950,16 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { // units.filter should return only one element (currentRun.units filter (_.source.file == aSym.sourceFile)).toList match { case List(unit) => - (unit.body find (_.symbol == aSym)) match { + // SI-4922 `sym == aSym` is insufficent if `aSym` is a clone of symbol + // of the parameter in the tree, as can happen with type parametric methods. + def isCorrespondingParam(sym: Symbol) = ( + sym != null && + sym != NoSymbol && + sym.owner == aSym.owner && + sym.name == aSym.name && + sym.isParamWithDefault + ) + (unit.body find (t => isCorrespondingParam(t.symbol))) match { case Some(ValDef(_,_,_,rhs)) => makeTree(rhs) case _ => None } @@ -1084,32 +1093,5 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { (bSym.isAliasType || bSym.isAbstractType) && { val rawComment = global.expandedDocComment(bSym, inTpl.sym) rawComment.contains("@template") || rawComment.contains("@documentable") } - - def findExternalLink(sym: Symbol, name: String): Option[LinkTo] = { - val sym1 = - if (sym == AnyClass || sym == AnyRefClass || sym == AnyValClass || sym == NothingClass) ListClass - else if (sym.isPackage) - /* Get package object which has associatedFile ne null */ - sym.info.member(newTermName("package")) - else sym - Option(sym1.associatedFile) flatMap (_.underlyingSource) flatMap { src => - val path = src.path - settings.extUrlMapping get path map { url => - LinkToExternal(name, url + "#" + name) - } - } orElse { - // Deprecated option. - settings.extUrlPackageMapping find { - case (pkg, _) => name startsWith pkg - } map { - case (_, url) => LinkToExternal(name, url + "#" + name) - } - } - } - - def externalSignature(sym: Symbol) = { - sym.info // force it, otherwise we see lazy types - (sym.nameString + sym.signatureString).replaceAll("\\s", "") - } } diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala index 5334de3797..f88251b22e 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala @@ -10,8 +10,6 @@ package scala.tools.nsc package doc package model -import comment._ - import scala.collection._ import scala.util.matching.Regex @@ -608,4 +606,4 @@ trait ModelFactoryImplicitSupport { false } else true // the member structure is different foo(3, 5) vs foo(3)(5) } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala index 942ccaf1ba..844a509b7e 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala @@ -4,8 +4,7 @@ package scala.tools.nsc package doc package model -import comment._ - +import base._ import diagram._ import scala.collection._ @@ -24,7 +23,8 @@ trait ModelFactoryTypeSupport { with ModelFactoryTypeSupport with DiagramFactory with CommentFactory - with TreeFactory => + with TreeFactory + with MemberLookup => import global._ import definitions.{ ObjectClass, NothingClass, AnyClass, AnyValClass, AnyRefClass } @@ -92,7 +92,10 @@ trait ModelFactoryTypeSupport { findTemplateMaybe(bSym) match { case Some(bTpl) if owner == bSym.owner => // (0) the owner's class is linked AND has a template - lovely - LinkToTpl(bTpl) + bTpl match { + case dtpl: DocTemplateEntity => new LinkToTpl(dtpl) + case _ => new Tooltip(bTpl.qualifiedName) + } case _ => val oTpl = findTemplateMaybe(owner) (oTpl, oTpl flatMap (findMember(bSym, _))) match { diff --git a/src/compiler/scala/tools/nsc/doc/model/TypeEntity.scala b/src/compiler/scala/tools/nsc/doc/model/TypeEntity.scala index e4a053e115..cf5c1fb3fb 100644 --- a/src/compiler/scala/tools/nsc/doc/model/TypeEntity.scala +++ b/src/compiler/scala/tools/nsc/doc/model/TypeEntity.scala @@ -20,7 +20,7 @@ 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, (LinkTo, Int)] + def refEntity: SortedMap[Int, (base.LinkTo, Int)] /** The human-readable representation of this type. */ override def toString = name diff --git a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala index 49cfaffc2e..cd60865ce7 100644 --- a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala +++ b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala @@ -3,7 +3,6 @@ package model package diagram import model._ -import comment.CommentFactory import java.util.regex.{Pattern, Matcher} import scala.util.matching.Regex @@ -259,4 +258,4 @@ trait DiagramDirectiveParser { result } -}
\ No newline at end of file +} 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 db2d0c0175..175b4a6472 100644 --- a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala @@ -3,7 +3,6 @@ package model package diagram import model._ -import comment.CommentFactory import scala.collection.mutable // statistics @@ -25,7 +24,7 @@ trait DiagramFactory extends DiagramDirectiveParser { // the following can used for hardcoding different relations into the diagram, for bootstrapping purposes def aggregationNode(text: String) = - NormalNode(new TypeEntity { val name = text; val refEntity = SortedMap[Int, (LinkTo, Int)]() }, None)() + NormalNode(new TypeEntity { val name = text; val refEntity = SortedMap[Int, (base.LinkTo, Int)]() }, None)() /** Create the inheritance diagram for this template */ def makeInheritanceDiagram(tpl: DocTemplateImpl): Option[Diagram] = { diff --git a/src/compiler/scala/tools/nsc/interactive/Doc.scala b/src/compiler/scala/tools/nsc/interactive/Doc.scala new file mode 100755 index 0000000000..ad28a28105 --- /dev/null +++ b/src/compiler/scala/tools/nsc/interactive/Doc.scala @@ -0,0 +1,59 @@ +/* NSC -- new Scala compiler + * Copyright 2007-2012 LAMP/EPFL + * @author Eugene Vigdorchik + */ + +package scala.tools.nsc +package interactive + +import doc.base._ +import comment._ +import scala.xml.NodeSeq + +sealed trait DocResult +final case class UrlResult(url: String) extends DocResult +final case class HtmlResult(comment: Comment) extends DocResult + +abstract class Doc(val settings: doc.Settings) extends MemberLookupBase with CommentFactoryBase { + + override val global: interactive.Global + import global._ + + def chooseLink(links: List[LinkTo]): LinkTo + + override def internalLink(sym: Symbol, site: Symbol): Option[LinkTo] = + ask { () => + if (sym.isClass || sym.isModule) + Some(LinkToTpl(sym)) + else + if ((site.isClass || site.isModule) && site.info.members.toList.contains(sym)) + Some(LinkToMember(sym, site)) + else + None + } + + override def toString(link: LinkTo) = ask { () => + link match { + case LinkToMember(mbr: Symbol, site: Symbol) => + mbr.signatureString + " in " + site.toString + case LinkToTpl(sym: Symbol) => sym.toString + case _ => link.toString + } + } + + def retrieve(sym: Symbol, site: Symbol): Option[DocResult] = { + val sig = ask { () => externalSignature(sym) } + findExternalLink(sym, sig) map { link => UrlResult(link.url) } orElse { + val resp = new Response[Tree] + // Ensure docComment tree is type-checked. + val pos = ask { () => docCommentPos(sym) } + askTypeAt(pos, resp) + resp.get.left.toOption flatMap { _ => + ask { () => + val comment = parseAtSymbol(expandedDocComment(sym), rawDocComment(sym), pos, Some(site)) + Some(HtmlResult(comment)) + } + } + } + } +} diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index 2ab389445f..0fc4fcaaf7 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -74,6 +74,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") if (verboseIDE) println("[%s][%s]".format(projectName, msg)) override def forInteractive = true + override def forScaladoc = settings.isScaladoc /** A map of all loaded files to the rich compilation units that correspond to them. */ diff --git a/src/compiler/scala/tools/nsc/interactive/RangePositions.scala b/src/compiler/scala/tools/nsc/interactive/RangePositions.scala index b95f1fa7ca..64117bd8ee 100644 --- a/src/compiler/scala/tools/nsc/interactive/RangePositions.scala +++ b/src/compiler/scala/tools/nsc/interactive/RangePositions.scala @@ -144,7 +144,7 @@ self: scala.tools.nsc.Global => */ private def setChildrenPos(pos: Position, trees: List[Tree]): Unit = try { for (tree <- trees) { - if (!tree.isEmpty && tree.pos == NoPosition) { + if (!tree.isEmpty && tree.canHaveAttrs && tree.pos == NoPosition) { val children = tree.children if (children.isEmpty) { tree setPos pos.focus @@ -165,7 +165,7 @@ self: scala.tools.nsc.Global => */ override def atPos[T <: Tree](pos: Position)(tree: T): T = { if (pos.isOpaqueRange) { - if (!tree.isEmpty && tree.pos == NoPosition) { + if (!tree.isEmpty && tree.canHaveAttrs && tree.pos == NoPosition) { tree.setPos(pos) val children = tree.children if (children.nonEmpty) { @@ -203,7 +203,7 @@ self: scala.tools.nsc.Global => def validate(tree: Tree, encltree: Tree): Unit = { - if (!tree.isEmpty) { + if (!tree.isEmpty && tree.canHaveAttrs) { if (settings.Yposdebug.value && (settings.verbose.value || settings.Yrangepos.value)) println("[%10s] %s".format("validate", treeStatus(tree, encltree))) diff --git a/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerInstance.scala b/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerInstance.scala index 5c1837b3bf..ea2333a65b 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerInstance.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerInstance.scala @@ -8,6 +8,7 @@ import scala.reflect.internal.util.Position /** Trait encapsulating the creation of a presentation compiler's instance.*/ private[tests] trait PresentationCompilerInstance extends TestSettings { protected val settings = new Settings + protected def docSettings: doc.Settings = new doc.Settings(_ => ()) protected val compilerReporter: CompilerReporter = new InteractiveReporter { override def compiler = PresentationCompilerInstance.this.compiler } @@ -28,4 +29,4 @@ private[tests] trait PresentationCompilerInstance extends TestSettings { reporter.println("\tbootClassPath: %s".format(settings.bootclasspath.value)) reporter.println("\tverbose: %b".format(settings.verbose.value)) } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala index 12fb8f1507..67519cf90c 100644 --- a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala +++ b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala @@ -120,7 +120,7 @@ trait MemberHandlers { if (replProps.vids) """" + " @ " + "%%8x".format(System.identityHashCode(%s)) + " """.trim.format(req fullPath name) else "" - """ + "%s%s: %s = " + %s""".format(prettyName, vidString, string2code(req typeOf name), resultString) + """ + "%s%s: %s = " + %s""".format(string2code(prettyName), vidString, string2code(req typeOf name), resultString) } } } @@ -147,8 +147,7 @@ trait MemberHandlers { override def resultExtractionCode(req: Request) = { val lhsType = string2code(req lookupTypeOf name) val res = string2code(req fullPath name) - - """ + "%s: %s = " + %s + "\n" """.format(lhs, lhsType, res) + "\n" + """ + "%s: %s = " + %s + "\n" """.format(string2code(lhs.toString), lhsType, res) + "\n" } } diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index 43a8402fc7..050f7a8f95 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -551,7 +551,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { if (parentToken == AT && in.token == DEFAULT) { val annot = atPos(pos) { - New(Select(scalaDot(nme.runtime), tpnme.AnnotationDefaultATTR), ListOfNil) + New(Select(scalaDot(nme.runtime), tpnme.AnnotationDefaultATTR), Nil) } mods1 = mods1 withAnnotations List(annot) skipTo(SEMI) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index b9eb1ba0cd..25b7813646 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -198,7 +198,7 @@ abstract class Pickler extends SubComponent { case RefinedType(parents, decls) => val rclazz = tp.typeSymbol for (m <- decls.iterator) - if (m.owner != rclazz) assert(false, "bad refinement member "+m+" of "+tp+", owner = "+m.owner) + if (m.owner != rclazz) abort("bad refinement member "+m+" of "+tp+", owner = "+m.owner) putSymbol(rclazz); putTypes(parents); putSymbols(decls.toList) case ClassInfoType(parents, decls, clazz) => putSymbol(clazz); putTypes(parents); putSymbols(decls.toList) diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index 4092d1262a..ec0797acb5 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -68,7 +68,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { def matchesName(param: Symbol) = param.name == name || param.name.startsWith(name + nme.NAME_JOIN_STRING) (constrParams filter matchesName) match { - case Nil => assert(false, name + " not in " + constrParams) ; null + case Nil => abort(name + " not in " + constrParams) case p :: _ => p } } @@ -511,7 +511,6 @@ abstract class Constructors extends Transform with ast.TreeDSL { sym = closureClass, constrMods = Modifiers(0), vparamss = List(List(outerFieldDef)), - argss = ListOfNil, body = List(applyMethodDef), superPos = impl.pos) } diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 64bb98e2c5..57bdaea17a 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -269,7 +269,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { /** Mix in members of implementation class mixinClass into class clazz */ def mixinImplClassMembers(mixinClass: Symbol, mixinInterface: Symbol) { - assert(mixinClass.isImplClass, "Not an impl class:" + + if (!mixinClass.isImplClass) debugwarn ("Impl class flag is not set " + ((mixinClass.debugLocationString, mixinInterface.debugLocationString))) for (member <- mixinClass.info.decls ; if isForwarded(member)) { @@ -360,7 +360,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // first complete the superclass with mixed in members addMixedinMembers(clazz.superClass, unit) - //Console.println("adding members of " + clazz.info.baseClasses.tail.takeWhile(superclazz !=) + " to " + clazz);//DEBUG for (mc <- clazz.mixinClasses ; if mc hasFlag lateINTERFACE) { // @SEAN: adding trait tracking so we don't have to recompile transitive closures unit.depends += mc @@ -871,8 +870,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val cond = Apply(Select(moduleVarRef, nme.eq), List(NULL)) mkFastPathBody(clazz, moduleSym, cond, List(assign), List(NULL), returnTree, attrThis, args) case _ => - assert(false, "Invalid getter " + rhs + " for module in class " + clazz) - EmptyTree + abort("Invalid getter " + rhs + " for module in class " + clazz) } def mkCheckedAccessor(clazz: Symbol, retVal: Tree, offset: Int, pos: Position, fieldSym: Symbol): Tree = { diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 2f79472cfb..bbab545d9e 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -965,6 +965,11 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case (NoSymbol, _) => None case (overridden, env) => val om = specializedOverload(clazz, overridden, env) + foreachWithIndex(om.paramss) { (params, i) => + foreachWithIndex(params) { (param, j) => + param.name = overriding.paramss(i)(j).name // SI-6555 Retain the parameter names from the subclass. + } + } debuglog("specialized overload %s for %s in %s: %s".format(om, overriding.name.decode, pp(env), om.info)) typeEnv(om) = env addConcreteSpecMethod(overriding) diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index 95cb052fda..a767850cba 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -327,8 +327,16 @@ abstract class TailCalls extends Transform { transformTrees(cases).asInstanceOf[List[CaseDef]] ) + case Try(block, catches, finalizer @ EmptyTree) => + // SI-1672 Catches are in tail position when there is no finalizer + treeCopy.Try(tree, + noTailTransform(block), + transformTrees(catches).asInstanceOf[List[CaseDef]], + EmptyTree + ) + case Try(block, catches, finalizer) => - // no calls inside a try are in tail position, but keep recursing for nested functions + // no calls inside a try are in tail position if there is a finalizer, but keep recursing for nested functions treeCopy.Try(tree, noTailTransform(block), noTailTransforms(catches).asInstanceOf[List[CaseDef]], diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 529009a058..838ea7d5a0 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -251,7 +251,10 @@ abstract class UnCurry extends InfoTransform val applyMethodDef = { val methSym = anonClass.newMethod(nme.apply, fun.pos, FINAL) - methSym setInfoAndEnter MethodType(methSym newSyntheticValueParams formals, restpe) + val paramSyms = map2(formals, fun.vparams) { + (tp, param) => methSym.newSyntheticValueParam(tp, param.name) + } + methSym setInfoAndEnter MethodType(paramSyms, restpe) fun.vparams foreach (_.symbol.owner = methSym) fun.body changeOwner (fun.symbol -> methSym) @@ -267,7 +270,7 @@ abstract class UnCurry extends InfoTransform localTyper.typedPos(fun.pos) { Block( - List(ClassDef(anonClass, NoMods, ListOfNil, ListOfNil, List(applyMethodDef), fun.pos)), + List(ClassDef(anonClass, NoMods, ListOfNil, List(applyMethodDef), fun.pos)), Typed(New(anonClass.tpe), TypeTree(fun.tpe))) } @@ -392,7 +395,7 @@ abstract class UnCurry extends InfoTransform localTyper.typedPos(fun.pos) { Block( - List(ClassDef(anonClass, NoMods, ListOfNil, ListOfNil, List(applyOrElseMethodDef, isDefinedAtMethodDef), fun.pos)), + List(ClassDef(anonClass, NoMods, ListOfNil, List(applyOrElseMethodDef, isDefinedAtMethodDef), fun.pos)), Typed(New(anonClass.tpe), TypeTree(fun.tpe))) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index faacb60d75..4268398081 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -184,14 +184,18 @@ trait ContextErrors { } def ParentTypesError(templ: Template, ex: TypeError) = { - templ.tpe = null - issueNormalTypeError(templ, ex.getMessage()) + templ.tpe = null + issueNormalTypeError(templ, ex.getMessage()) + setError(templ) } // additional parentTypes errors - def ConstrArgsInTraitParentTpeError(arg: Tree, parent: Symbol) = + def ConstrArgsInParentWhichIsTraitError(arg: Tree, parent: Symbol) = issueNormalTypeError(arg, parent + " is a trait; does not take constructor arguments") + def ConstrArgsInParentOfTraitError(arg: Tree, parent: Symbol) = + issueNormalTypeError(arg, "parents of traits may not have parameters") + def MissingTypeArgumentsParentTpeError(supertpt: Tree) = issueNormalTypeError(supertpt, "missing type arguments") @@ -976,7 +980,7 @@ trait ContextErrors { object SymValidateErrors extends Enumeration { val ImplicitConstr, ImplicitNotTermOrClass, ImplicitAtToplevel, OverrideClass, SealedNonClass, AbstractNonClass, - OverrideConstr, AbstractOverride, LazyAndEarlyInit, + OverrideConstr, AbstractOverride, AbstractOverrideOnTypeMember, LazyAndEarlyInit, ByNameParameter, AbstractVar = Value } @@ -1043,9 +1047,6 @@ trait ContextErrors { def MaxParametersCaseClassError(tree: Tree) = issueNormalTypeError(tree, "Implementation restriction: case classes cannot have more than " + definitions.MaxFunctionArity + " parameters.") - def InheritsItselfError(tree: Tree) = - issueNormalTypeError(tree, tree.tpe.typeSymbol+" inherits itself") - def MissingParameterOrValTypeError(vparam: Tree) = issueNormalTypeError(vparam, "missing parameter type") @@ -1078,6 +1079,9 @@ trait ContextErrors { case AbstractOverride => "`abstract override' modifier only allowed for members of traits" + case AbstractOverrideOnTypeMember => + "`abstract override' modifier not allowed for type members" + case LazyAndEarlyInit => "`lazy' definitions may not be initialized early" @@ -1279,7 +1283,10 @@ trait ContextErrors { fail() } - private def implRefError(message: String) = genericError(methPart(macroDdef.rhs), message) + private def implRefError(message: String) = { + val treeInfo.Applied(implRef, _, _) = macroDdef.rhs + genericError(implRef, message) + } private def compatibilityError(message: String) = implRefError( diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 507825ff15..0907f1088a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -510,8 +510,8 @@ trait Contexts { self: Analyzer => /* var c = this while (c != NoContext && c.owner != owner) { - if (c.outer eq null) assert(false, "accessWithin(" + owner + ") " + c);//debug - if (c.outer.enclClass eq null) assert(false, "accessWithin(" + owner + ") " + c);//debug + if (c.outer eq null) abort("accessWithin(" + owner + ") " + c);//debug + if (c.outer.enclClass eq null) abort("accessWithin(" + owner + ") " + c);//debug c = c.outer.enclClass } c != NoContext diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index fc10f68454..70a736c91f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -149,9 +149,20 @@ trait Implicits { class SearchResult(val tree: Tree, val subst: TreeTypeSubstituter) { override def toString = "SearchResult(%s, %s)".format(tree, if (subst.isEmpty) "" else subst) + + def isFailure = false + def isAmbiguousFailure = false + final def isSuccess = !isFailure } - lazy val SearchFailure = new SearchResult(EmptyTree, EmptyTreeTypeSubstituter) + lazy val SearchFailure = new SearchResult(EmptyTree, EmptyTreeTypeSubstituter) { + override def isFailure = true + } + + lazy val AmbiguousSearchFailure = new SearchResult(EmptyTree, EmptyTreeTypeSubstituter) { + override def isFailure = true + override def isAmbiguousFailure = true + } /** A class that records an available implicit * @param name The name of the implicit @@ -831,7 +842,7 @@ trait Implicits { catch divergenceHandler tryImplicitInfo(i) match { - case SearchFailure => + case sr if sr.isFailure => // We don't want errors that occur during checking implicit info // to influence the check of further infos. context.condBufferFlush(_.kind != ErrorKinds.Divergent) @@ -871,14 +882,14 @@ trait Implicits { rest find (alt => !improves(chosen, alt)) match { case Some(competing) => AmbiguousImplicitError(chosen, competing, "both", "and", "")(isView, pt, tree)(context) - return SearchFailure // Stop the search once ambiguity is encountered, see t4457_2.scala + return AmbiguousSearchFailure // Stop the search once ambiguity is encountered, see t4457_2.scala case _ => if (isView) chosen.useCountView += 1 else chosen.useCountArg += 1 } } - if (best == SearchFailure) { + if (best.isFailure) { /** If there is no winner, and we witnessed and caught divergence, * now we can throw it for the error message. */ @@ -935,8 +946,8 @@ trait Implicits { * - for alias types and abstract types, we take instead the parts * - of their upper bounds. * @return For those parts that refer to classes with companion objects that - * can be accessed with unambiguous stable prefixes, the implicits infos - * which are members of these companion objects. + * can be accessed with unambiguous stable prefixes that are not existentially + * bound, the implicits infos which are members of these companion objects. */ private def companionImplicitMap(tp: Type): InfoMap = { @@ -952,7 +963,7 @@ trait Implicits { infoMap(sym) = List() // ambiguous prefix - ignore implicit members } case None => - if (pre.isStable) { + if (pre.isStable && !pre.typeSymbol.isExistentiallyBound) { val companion = companionSymbolOf(sym, context) companion.moduleClass match { case mc: ModuleClassSymbol => @@ -1377,24 +1388,25 @@ trait Implicits { var result = searchImplicit(context.implicitss, true) - if (result == SearchFailure) { + if (result.isFailure) { if (Statistics.canEnable) Statistics.stopTimer(inscopeFailNanos, failstart) } else { if (Statistics.canEnable) Statistics.stopTimer(inscopeSucceedNanos, succstart) if (Statistics.canEnable) Statistics.incCounter(inscopeImplicitHits) } - if (result == SearchFailure) { + if (result.isFailure) { val previousErrs = context.flushAndReturnBuffer() val failstart = if (Statistics.canEnable) Statistics.startTimer(oftypeFailNanos) else null val succstart = if (Statistics.canEnable) Statistics.startTimer(oftypeSucceedNanos) else null + val wasAmbigious = result.isAmbiguousFailure // SI-6667, never search companions after an ambiguous error in in-scope implicits result = materializeImplicit(pt) // `materializeImplicit` does some preprocessing for `pt` // is it only meant for manifests/tags or we need to do the same for `implicitsOfExpectedType`? - if (result == SearchFailure) result = searchImplicit(implicitsOfExpectedType, false) + if (result.isFailure && !wasAmbigious) result = searchImplicit(implicitsOfExpectedType, false) - if (result == SearchFailure) { + if (result.isFailure) { context.updateBuffer(previousErrs) if (Statistics.canEnable) Statistics.stopTimer(oftypeFailNanos, failstart) } else { @@ -1403,7 +1415,7 @@ trait Implicits { } } - if (result == SearchFailure && settings.debug.value) + if (result.isFailure && settings.debug.value) log("no implicits found for "+pt+" "+pt.typeSymbol.info.baseClasses+" "+implicitsOfExpectedType) result diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 5deed4ffee..7ae8923e43 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -221,7 +221,7 @@ trait Infer extends Checkable { // such as T <: T gets completed. See #360 tvar.constr.inst = ErrorType else - assert(false, tvar.origin+" at "+tvar.origin.typeSymbol.owner) + abort(tvar.origin+" at "+tvar.origin.typeSymbol.owner) } tvars map instantiate } @@ -1003,22 +1003,22 @@ trait Infer extends Checkable { */ /** error if arguments not within bounds. */ def checkBounds(tree: Tree, pre: Type, owner: Symbol, - tparams: List[Symbol], targs: List[Type], prefix: String): Boolean = { - //@M validate variances & bounds of targs wrt variances & bounds of tparams - //@M TODO: better place to check this? - //@M TODO: errors for getters & setters are reported separately - val kindErrors = checkKindBounds(tparams, targs, pre, owner) - - if(!kindErrors.isEmpty) { - if (targs contains WildcardType) true - else { KindBoundErrors(tree, prefix, targs, tparams, kindErrors); false } - } else if (!isWithinBounds(pre, owner, tparams, targs)) { - if (!(targs exists (_.isErroneous)) && !(tparams exists (_.isErroneous))) { - NotWithinBounds(tree, prefix, targs, tparams, kindErrors) - false - } else true - } else true - } + tparams: List[Symbol], targs: List[Type], prefix: String): Boolean = + if ((targs exists (_.isErroneous)) || (tparams exists (_.isErroneous))) true + else { + //@M validate variances & bounds of targs wrt variances & bounds of tparams + //@M TODO: better place to check this? + //@M TODO: errors for getters & setters are reported separately + val kindErrors = checkKindBounds(tparams, targs, pre, owner) + kindErrors match { + case Nil => + def notWithinBounds() = NotWithinBounds(tree, prefix, targs, tparams, Nil) + isWithinBounds(pre, owner, tparams, targs) || {notWithinBounds(); false} + case errors => + def kindBoundErrors() = KindBoundErrors(tree, prefix, targs, tparams, errors) + (targs contains WildcardType) || {kindBoundErrors(); false} + } + } def checkKindBounds(tparams: List[Symbol], targs: List[Type], pre: Type, owner: Symbol): List[String] = { checkKindBounds0(tparams, targs, pre, owner, true) map { diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 36edd46f25..98b6264051 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -847,13 +847,8 @@ trait Namers extends MethodSynthesis { private def templateSig(templ: Template): Type = { val clazz = context.owner def checkParent(tpt: Tree): Type = { - val tp = tpt.tpe - val inheritsSelf = tp.typeSymbol == owner - if (inheritsSelf) - InheritsItselfError(tpt) - - if (inheritsSelf || tp.isError) AnyRefClass.tpe - else tp + if (tpt.tpe.isError) AnyRefClass.tpe + else tpt.tpe } val parents = typer.parentTypes(templ) map checkParent @@ -1260,7 +1255,11 @@ trait Namers extends MethodSynthesis { case defn: MemberDef => val ainfos = defn.mods.annotations filterNot (_ eq null) map { ann => // need to be lazy, #1782. beforeTyper to allow inferView in annotation args, SI-5892. - AnnotationInfo lazily beforeTyper(typer typedAnnotation ann) + AnnotationInfo lazily { + val context1 = typer.context.make(ann) + context1.setReportErrors() + beforeTyper(newTyper(context1) typedAnnotation ann) + } } if (ainfos.nonEmpty) { annotated setAnnotations ainfos @@ -1444,8 +1443,12 @@ trait Namers extends MethodSynthesis { if (sym.isConstructor && sym.isAnyOverride) fail(OverrideConstr) - if (sym.isAbstractOverride && !sym.owner.isTrait) - fail(AbstractOverride) + if (sym.isAbstractOverride) { + if (!sym.owner.isTrait) + fail(AbstractOverride) + if(sym.isType) + fail(AbstractOverrideOnTypeMember) + } if (sym.isLazy && sym.hasFlag(PRESUPER)) fail(LazyAndEarlyInit) if (sym.info.typeSymbol == FunctionClass(0) && sym.isValueParameter && sym.owner.isCaseClass) diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index 834c64aaae..fa8aff5cdd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -801,8 +801,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL protected def spliceApply(binder: Symbol): Tree = { object splice extends Transformer { override def transform(t: Tree) = t match { - case Apply(x, List(Ident(nme.SELECTOR_DUMMY))) => - treeCopy.Apply(t, x, List(CODE.REF(binder))) + case Apply(x, List(i @ Ident(nme.SELECTOR_DUMMY))) => + treeCopy.Apply(t, x, List(CODE.REF(binder).setPos(i.pos))) case _ => super.transform(t) } } @@ -879,7 +879,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL override def transform(tree: Tree): Tree = { def subst(from: List[Symbol], to: List[Tree]): Tree = if (from.isEmpty) tree - else if (tree.symbol == from.head) typedIfOrigTyped(to.head.shallowDuplicate, tree.tpe) + else if (tree.symbol == from.head) typedIfOrigTyped(to.head.shallowDuplicate.setPos(tree.pos), tree.tpe) else subst(from.tail, to.tail) tree match { diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 78ec6508ed..9bd3aa8fe5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1264,13 +1264,15 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans val cdef = ClassDef(mods | MODULE, name.toTypeName, Nil, impl) setSymbol classSym setType NoType def findOrCreateModuleVar() = localTyper.typedPos(tree.pos) { - lazy val createModuleVar = gen.mkModuleVarDef(sym) - sym.enclClass.info.decl(nme.moduleVarName(sym.name.toTermName)) match { - // In case we are dealing with local symbol then we already have - // to correct error with forward reference - case NoSymbol => createModuleVar - case vsym => ValDef(vsym) - } + // See SI-5012, SI-6712. + val vsym = ( + if (sym.owner.isTerm) NoSymbol + else sym.enclClass.info.decl(nme.moduleVarName(sym.name.toTermName)) + ) + // In case we are dealing with local symbol then we already have + // to correct error with forward reference + if (vsym == NoSymbol) gen.mkModuleVarDef(sym) + else ValDef(vsym) } def createStaticModuleAccessor() = afterRefchecks { val method = ( @@ -1534,8 +1536,14 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans sym.name == nme.apply && isClassTypeAccessible(tree) - if (doTransform) + if (doTransform) { + tree foreach { + case i@Ident(_) => + enterReference(i.pos, i.symbol) // SI-5390 need to `enterReference` for `a` in `a.B()` + case _ => + } toConstructor(tree.pos, tree.tpe) + } else { ifNot tree @@ -1671,7 +1679,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans checkAnyValSubclass(currentOwner) if (bridges.nonEmpty) deriveTemplate(tree)(_ ::: bridges) else tree - case dc@TypeTreeWithDeferredRefCheck() => assert(false, "adapt should have turned dc: TypeTreeWithDeferredRefCheck into tpt: TypeTree, with tpt.original == dc"); dc + case dc@TypeTreeWithDeferredRefCheck() => abort("adapt should have turned dc: TypeTreeWithDeferredRefCheck into tpt: TypeTree, with tpt.original == dc") case tpt@TypeTree() => if(tpt.original != null) { tpt.original foreach { diff --git a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala index 64c5b41638..20db479463 100644 --- a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala +++ b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala @@ -4,7 +4,31 @@ package typechecker trait StdAttachments { self: Analyzer => + import global._ + + /** Carries information necessary to expand the host tree. + * At times we need to store this info, because macro expansion can be delayed until its targs are inferred. + * After a macro application has been successfully expanded, this attachment is destroyed. + */ type UnaffiliatedMacroContext = scala.reflect.macros.runtime.Context type MacroContext = UnaffiliatedMacroContext { val universe: self.global.type } case class MacroRuntimeAttachment(delayed: Boolean, typerContext: Context, macroContext: Option[MacroContext]) + + /** After being synthesized by the parser, primary constructors aren't fully baked yet. + * A call to super in such constructors is just a fill-me-in-later dummy resolved later + * by `parentTypes`. This attachment coordinates `parentTypes` and `typedTemplate` and + * allows them to complete the synthesis. + */ + case class SuperArgsAttachment(argss: List[List[Tree]]) + + /** Convenience method for `SuperArgsAttachment`. + * Compared with `MacroRuntimeAttachment` this attachment has different a usage pattern, + * so it really benefits from a dedicated extractor. + */ + def superArgs(tree: Tree): Option[List[List[Tree]]] = + tree.attachments.get[SuperArgsAttachment] collect { case SuperArgsAttachment(argss) => argss } + + /** Determines whether the given tree has an associated SuperArgsAttachment. + */ + def hasSuperArgs(tree: Tree): Boolean = superArgs(tree).nonEmpty }
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index ae506cd6ab..e45d64ade5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -48,12 +48,15 @@ trait Typers extends Modes with Adaptations with Tags { resetContexts() resetImplicits() transformed.clear() + clearDocComments() } object UnTyper extends Traverser { override def traverse(tree: Tree) = { - if (tree != EmptyTree) tree.tpe = null - if (tree.hasSymbol) tree.symbol = NoSymbol + if (tree.canHaveAttrs) { + tree.tpe = null + if (tree.hasSymbol) tree.symbol = NoSymbol + } super.traverse(tree) } } @@ -132,7 +135,7 @@ trait Typers extends Modes with Adaptations with Tags { val res = if (paramFailed || (paramTp.isError && {paramFailed = true; true})) SearchFailure else inferImplicit(fun, paramTp, context.reportErrors, false, context) argResultsBuff += res - if (res != SearchFailure) { + if (res.isSuccess) { argBuff += mkArg(res.tree, param.name) } else { mkArg = mkNamedArg // don't pass the default argument (if any) here, but start emitting named arguments for the following args @@ -761,7 +764,7 @@ trait Typers extends Modes with Adaptations with Tags { featureTrait.owner.ownerChain.takeWhile(_ != languageFeatureModule.moduleClass).reverse val featureName = (nestedOwners map (_.name + ".")).mkString + featureTrait.name def action(): Boolean = { - def hasImport = inferImplicit(EmptyTree: Tree, featureTrait.tpe, true, false, context) != SearchFailure + def hasImport = inferImplicit(EmptyTree: Tree, featureTrait.tpe, true, false, context).isSuccess def hasOption = settings.language.value exists (s => s == featureName || s == "_") val OK = hasImport || hasOption if (!OK) { @@ -1378,13 +1381,6 @@ trait Typers extends Modes with Adaptations with Tags { if (member(qual, name) != NoSymbol) qual else adaptToMember(qual, HasMember(name)) - private def typePrimaryConstrBody(clazz : Symbol, cbody: Tree, tparams: List[Symbol], enclTparams: List[Symbol], vparamss: List[List[ValDef]]): Tree = { - // XXX: see about using the class's symbol.... - enclTparams foreach (sym => context.scope.enter(sym)) - namer.enterValueParams(vparamss) - typed(cbody) - } - private def validateNoCaseAncestor(clazz: Symbol) = { if (!phase.erasedTypes) { for (ancestor <- clazz.ancestors find (_.isCase)) { @@ -1486,125 +1482,243 @@ trait Typers extends Modes with Adaptations with Tags { unit.error(tparam.pos, "type parameter of value class may not be specialized") } - def parentTypes(templ: Template): List[Tree] = - 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. - var supertpt = typedTypeConstructor(templ.parents.head) - val firstParent = supertpt.tpe.typeSymbol - var mixins = templ.parents.tail map typedType - // If first parent is a trait, make it first mixin and add its superclass as first parent - while ((supertpt.tpe.typeSymbol ne null) && supertpt.tpe.typeSymbol.initialize.isTrait) { - val supertpt1 = typedType(supertpt) - if (!supertpt1.isErrorTyped) { - mixins = supertpt1 :: mixins - supertpt = TypeTree(supertpt1.tpe.firstParent) setPos supertpt.pos.focus + /** Typechecks a parent type reference. + * + * This typecheck is harder than it might look, because it should honor early + * definitions and also perform type argument inference with the help of super call + * arguments provided in `encodedtpt`. + * + * The method is called in batches (batch = 1 time per each parent type referenced), + * two batches per definition: once from namer, when entering a ClassDef or a ModuleDef + * and once from typer, when typechecking the definition. + * + * ***Arguments*** + * + * `encodedtpt` represents the parent type reference wrapped in an `Apply` node + * which indicates value arguments (i.e. type macro arguments or super constructor call arguments) + * If no value arguments are provided by the user, the `Apply` node is still + * there, but its `args` will be set to `Nil`. + * This argument is synthesized by `tools.nsc.ast.Parsers.templateParents`. + * + * `templ` is an enclosing template, which contains a primary constructor synthesized by the parser. + * Such a constructor is a DefDef which contains early initializers and maybe a super constructor call + * (I wrote "maybe" because trait constructors don't call super constructors). + * This argument is synthesized by `tools.nsc.ast.Trees.Template`. + * + * `inMixinPosition` indicates whether the reference is not the first in the + * list of parents (and therefore cannot be a class) or the opposite. + * + * ***Return value and side effects*** + * + * Returns a `TypeTree` representing a resolved parent type. + * If the typechecked parent reference implies non-nullary and non-empty argument list, + * this argument list is attached to the returned value in SuperArgsAttachment. + * The attachment is necessary for the subsequent typecheck to fixup a super constructor call + * in the body of the primary constructor (see `typedTemplate` for details). + * + * This method might invoke `typedPrimaryConstrBody`, hence it might cause the side effects + * described in the docs of that method. It might also attribute the Super(_, _) reference + * (if present) inside the primary constructor of `templ`. + * + * ***Example*** + * + * For the following definition: + * + * class D extends { + * val x = 2 + * val y = 4 + * } with B(x)(3) with C(y) with T + * + * this method will be called six times: + * + * (3 times from the namer) + * typedParentType(Apply(Apply(Ident(B), List(Ident(x))), List(3)), templ, inMixinPosition = false) + * typedParentType(Apply(Ident(C), List(Ident(y))), templ, inMixinPosition = true) + * typedParentType(Apply(Ident(T), List()), templ, inMixinPosition = true) + * + * (3 times from the typer) + * <the same three calls> + */ + private def typedParentType(encodedtpt: Tree, templ: Template, inMixinPosition: Boolean): Tree = { + val app = treeInfo.dissectApplied(encodedtpt) + val (treeInfo.Applied(core, targs, argss), decodedtpt) = (app, app.callee) + val argssAreTrivial = argss == Nil || argss == ListOfNil + + // we cannot avoid cyclic references with `initialize` here, because when type macros arrive, + // we'll have to check the probe for isTypeMacro anyways. + // therefore I think it's reasonable to trade a more specific "inherits itself" error + // for a generic, yet understandable "cyclic reference" error + var probe = typedTypeConstructor(core.duplicate).tpe.typeSymbol + if (probe == null) probe = NoSymbol + probe.initialize + + if (probe.isTrait || inMixinPosition) { + if (!argssAreTrivial) { + if (probe.isTrait) ConstrArgsInParentWhichIsTraitError(encodedtpt, probe) + else () // a class in a mixin position - this warrants an error in `validateParentClasses` + // therefore here we do nothing, e.g. don't check that the # of ctor arguments + // matches the # of ctor parameters or stuff like that + } + typedType(decodedtpt) + } else { + var supertpt = typedTypeConstructor(decodedtpt) + val supertparams = if (supertpt.hasSymbol) supertpt.symbol.typeParams else Nil + if (supertparams.nonEmpty) { + typedPrimaryConstrBody(templ) { + val supertpe = PolyType(supertparams, appliedType(supertpt.tpe, supertparams map (_.tpeHK))) + val supercall = New(supertpe, mmap(argss)(_.duplicate)) + val treeInfo.Applied(Select(ctor, nme.CONSTRUCTOR), _, _) = supercall + ctor setType supertpe // this is an essential hack, otherwise it will occasionally fail to typecheck + atPos(supertpt.pos.focus)(supercall) + } match { + case EmptyTree => MissingTypeArgumentsParentTpeError(supertpt) + case tpt => supertpt = TypeTree(tpt.tpe) setPos supertpt.pos.focus } } - if (supertpt.tpe.typeSymbol == AnyClass && firstParent.isTrait) - supertpt.tpe = AnyRefClass.tpe - - // Determine - // - supertparams: Missing type parameters from supertype - // - supertpe: Given supertype, polymorphic in supertparams - val supertparams = if (supertpt.hasSymbol) supertpt.symbol.typeParams else List() - var supertpe = supertpt.tpe - if (!supertparams.isEmpty) - supertpe = PolyType(supertparams, appliedType(supertpe, supertparams map (_.tpeHK))) - - // A method to replace a super reference by a New in a supercall - def transformSuperCall(scall: Tree): Tree = (scall: @unchecked) match { - case Apply(fn, args) => - treeCopy.Apply(scall, transformSuperCall(fn), args map (_.duplicate)) - case Select(Super(_, _), nme.CONSTRUCTOR) => - treeCopy.Select( - scall, - atPos(supertpt.pos.focus)(New(TypeTree(supertpe)) setType supertpe), - nme.CONSTRUCTOR) - } - - treeInfo.firstConstructor(templ.body) match { - case constr @ DefDef(_, _, _, vparamss, _, cbody @ Block(cstats, cunit)) => - // Convert constructor body to block in environment and typecheck it - val (preSuperStats, superCall) = { - val (stats, rest) = cstats span (x => !treeInfo.isSuperConstrCall(x)) - (stats map (_.duplicate), if (rest.isEmpty) EmptyTree else rest.head.duplicate) - } - val cstats1 = if (superCall == EmptyTree) preSuperStats else preSuperStats :+ superCall - val cbody1 = treeCopy.Block(cbody, preSuperStats, superCall match { - case Apply(_, _) if supertparams.nonEmpty => transformSuperCall(superCall) - case _ => cunit.duplicate - }) - val outercontext = context.outer - - assert(clazz != NoSymbol, templ) - val cscope = outercontext.makeNewScope(constr, outercontext.owner) - val cbody2 = newTyper(cscope) // called both during completion AND typing. - .typePrimaryConstrBody(clazz, - cbody1, supertparams, clazz.unsafeTypeParams, vparamss map (_.map(_.duplicate))) - - superCall match { - case Apply(_, _) => - val sarg = treeInfo.firstArgument(superCall) - if (sarg != EmptyTree && supertpe.typeSymbol != firstParent) - ConstrArgsInTraitParentTpeError(sarg, firstParent) - if (!supertparams.isEmpty) - supertpt = TypeTree(cbody2.tpe) setPos supertpt.pos.focus - case _ => - if (!supertparams.isEmpty) - MissingTypeArgumentsParentTpeError(supertpt) - } + // this is the place where we tell the typer what argss should be used for the super call + // if argss are nullary or empty, then (see the docs for `typedPrimaryConstrBody`) + // the super call dummy is already good enough, so we don't need to do anything + if (argssAreTrivial) supertpt else supertpt updateAttachment SuperArgsAttachment(argss) + } + } - val preSuperVals = treeInfo.preSuperFields(templ.body) - if (preSuperVals.isEmpty && preSuperStats.nonEmpty) - debugwarn("Wanted to zip empty presuper val list with " + preSuperStats) - else - map2(preSuperStats, preSuperVals)((ldef, gdef) => gdef.tpt.tpe = ldef.symbol.tpe) + /** Typechecks the mishmash of trees that happen to be stuffed into the primary constructor of a given template. + * Before commencing the typecheck, replaces the `pendingSuperCall` dummy with the result of `actualSuperCall`. + * `actualSuperCall` can return `EmptyTree`, in which case the dummy is replaced with a literal unit. + * + * ***Return value and side effects*** + * + * If a super call is present in the primary constructor and is not erased by the transform, returns it typechecked. + * Otherwise (e.g. if the primary constructor is missing or the super call isn't there) returns `EmptyTree`. + * + * As a side effect, this method attributes the underlying fields of early vals. + * Early vals aren't typechecked anywhere else, so it's essential to call `typedPrimaryConstrBody` + * at least once per definition. It'd be great to disentangle this logic at some point. + * + * ***Example*** + * + * For the following definition: + * + * class D extends { + * val x = 2 + * val y = 4 + * } with B(x)(3) with C(y) with T + * + * the primary constructor of `templ` will be: + * + * Block(List( + * ValDef(NoMods, x, TypeTree(), 2) + * ValDef(NoMods, y, TypeTree(), 4) + * global.pendingSuperCall, + * Literal(Constant(()))) + * + * Note the `pendingSuperCall` part. This is the representation of a fill-me-in-later supercall dummy, + * which encodes the fact that supercall argss are unknown during parsing and need to be transplanted + * from one of the parent types. Read more about why the argss are unknown in `tools.nsc.ast.Trees.Template`. + */ + private def typedPrimaryConstrBody(templ: Template)(actualSuperCall: => Tree): Tree = + treeInfo.firstConstructor(templ.body) match { + case ctor @ DefDef(_, _, _, vparamss, _, cbody @ Block(cstats, cunit)) => + val (preSuperStats, superCall) = { + val (stats, rest) = cstats span (x => !treeInfo.isSuperConstrCall(x)) + (stats map (_.duplicate), if (rest.isEmpty) EmptyTree else rest.head.duplicate) + } + val superCall1 = (superCall match { + case global.pendingSuperCall => actualSuperCall + case EmptyTree => EmptyTree + }) orElse cunit + val cbody1 = treeCopy.Block(cbody, preSuperStats, superCall1) + + val clazz = context.owner + assert(clazz != NoSymbol, templ) + val cscope = context.outer.makeNewScope(ctor, context.outer.owner) + val cbody2 = { // called both during completion AND typing. + val typer1 = newTyper(cscope) + // XXX: see about using the class's symbol.... + clazz.unsafeTypeParams foreach (sym => typer1.context.scope.enter(sym)) + typer1.namer.enterValueParams(vparamss map (_.map(_.duplicate))) + typer1.typed(cbody1) + } - case _ => - if (!supertparams.isEmpty) - MissingTypeArgumentsParentTpeError(supertpt) - } -/* experimental: early types as type arguments - val hasEarlyTypes = templ.body exists (treeInfo.isEarlyTypeDef) - val earlyMap = new EarlyMap(clazz) - List.mapConserve(supertpt :: mixins){ tpt => - val tpt1 = checkNoEscaping.privates(clazz, tpt) - if (hasEarlyTypes) tpt1 else tpt1 setType earlyMap(tpt1.tpe) - } -*/ + val preSuperVals = treeInfo.preSuperFields(templ.body) + if (preSuperVals.isEmpty && preSuperStats.nonEmpty) + debugwarn("Wanted to zip empty presuper val list with " + preSuperStats) + else + map2(preSuperStats, preSuperVals)((ldef, gdef) => gdef.tpt.tpe = ldef.symbol.tpe) - //Console.println("parents("+clazz") = "+supertpt :: mixins);//DEBUG + if (superCall1 == cunit) EmptyTree else cbody2 + case _ => + EmptyTree + } - // Certain parents are added in the parser before it is known whether - // that class also declared them as parents. For instance, this is an - // error unless we take corrective action here: - // - // case class Foo() extends Serializable - // - // So we strip the duplicates before typer. - def fixDuplicates(remaining: List[Tree]): List[Tree] = remaining match { - case Nil => Nil - case x :: xs => - val sym = x.symbol - x :: fixDuplicates( - if (isPossibleSyntheticParent(sym)) xs filterNot (_.symbol == sym) - else xs - ) + /** Makes sure that the first type tree in the list of parent types is always a class. + * If the first parent is a trait, prepend its supertype to the list until it's a class. + */ + private def normalizeFirstParent(parents: List[Tree]): List[Tree] = parents match { + case first :: rest if treeInfo.isTraitRef(first) => + def explode(supertpt: Tree, acc: List[Tree]): List[Tree] = { + if (treeInfo.isTraitRef(supertpt)) { + val supertpt1 = typedType(supertpt) + if (!supertpt1.isErrorTyped) { + val supersupertpt = TypeTree(supertpt1.tpe.firstParent) setPos supertpt.pos.focus + return explode(supersupertpt, supertpt1 :: acc) + } + } + if (supertpt.tpe.typeSymbol == AnyClass) supertpt.tpe = AnyRefClass.tpe + supertpt :: acc } + explode(first, Nil) ++ rest + case _ => parents + } - fixDuplicates(supertpt :: mixins) mapConserve (tpt => checkNoEscaping.privates(clazz, tpt)) - } - catch { - case ex: TypeError => - // fallback in case of cyclic errors - // @H none of the tests enter here but I couldn't rule it out - log("Type error calculating parents in template " + templ) - log("Error: " + ex) - ParentTypesError(templ, ex) - List(TypeTree(AnyRefClass.tpe)) - } + /** Certain parents are added in the parser before it is known whether + * that class also declared them as parents. For instance, this is an + * error unless we take corrective action here: + * + * case class Foo() extends Serializable + * + * So we strip the duplicates before typer. + */ + private def fixDuplicateSyntheticParents(parents: List[Tree]): List[Tree] = parents match { + case Nil => Nil + case x :: xs => + val sym = x.symbol + x :: fixDuplicateSyntheticParents( + if (isPossibleSyntheticParent(sym)) xs filterNot (_.symbol == sym) + else xs + ) + } + + def parentTypes(templ: Template): List[Tree] = templ.parents match { + case Nil => List(atPos(templ.pos)(TypeTree(AnyRefClass.tpe))) + case first :: rest => + try { + val supertpts = fixDuplicateSyntheticParents(normalizeFirstParent( + typedParentType(first, templ, inMixinPosition = false) +: + (rest map (typedParentType(_, templ, inMixinPosition = true))))) + + // if that is required to infer the targs of a super call + // typedParentType calls typedPrimaryConstrBody to do the inferring typecheck + // as a side effect, that typecheck also assigns types to the fields underlying early vals + // however if inference is not required, the typecheck doesn't happen + // and therefore early fields have their type trees not assigned + // here we detect this situation and take preventive measures + if (treeInfo.hasUntypedPreSuperFields(templ.body)) + typedPrimaryConstrBody(templ)(EmptyTree) + + supertpts mapConserve (tpt => checkNoEscaping.privates(context.owner, tpt)) + } catch { + case ex: TypeError => + // fallback in case of cyclic errors + // @H none of the tests enter here but I couldn't rule it out + // upd. @E when a definitions inherits itself, we end up here + // because `typedParentType` triggers `initialize` for parent types symbols + log("Type error calculating parents in template " + templ) + log("Error: " + ex) + ParentTypesError(templ, ex) + List(TypeTree(AnyRefClass.tpe)) + } + } /** <p>Check that</p> * <ul> @@ -1816,8 +1930,7 @@ trait Typers extends Modes with Adaptations with Tags { */ def typedTemplate(templ: Template, parents1: List[Tree]): Template = { val clazz = context.owner - // complete lazy annotations - val annots = clazz.annotations + clazz.annotations.map(_.completeInfo) if (templ.symbol == NoSymbol) templ setSymbol clazz.newLocalDummy(templ.pos) val self1 = templ.self match { @@ -1844,9 +1957,12 @@ trait Typers extends Modes with Adaptations with Tags { // the following is necessary for templates generated later assert(clazz.info.decls != EmptyScope, clazz) enterSyms(context.outer.make(templ, clazz, clazz.info.decls), templ.body) - validateParentClasses(parents1, selfType) + if (!templ.isErrorTyped) // if `parentTypes` has invalidated the template, don't validate it anymore + validateParentClasses(parents1, selfType) if (clazz.isCase) validateNoCaseAncestor(clazz) + if (clazz.isTrait && hasSuperArgs(parents1.head)) + ConstrArgsInParentOfTraitError(parents1.head, clazz) if ((clazz isSubClass ClassfileAnnotationClass) && !clazz.owner.isPackageClass) unit.error(clazz.pos, "inner classes cannot be classfile annotations") @@ -1854,9 +1970,21 @@ trait Typers extends Modes with Adaptations with Tags { if (!phase.erasedTypes && !clazz.info.resultType.isError) // @S: prevent crash for duplicated type members checkFinitary(clazz.info.resultType.asInstanceOf[ClassInfoType]) - val body = - if (isPastTyper || reporter.hasErrors) templ.body - else templ.body flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _)) + val body = { + val body = + if (isPastTyper || reporter.hasErrors) templ.body + else templ.body flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _)) + val primaryCtor = treeInfo.firstConstructor(body) + val primaryCtor1 = primaryCtor match { + case DefDef(_, _, _, _, _, Block(earlyVals :+ global.pendingSuperCall, unit)) => + val argss = superArgs(parents1.head) getOrElse Nil + val pos = wrappingPos(parents1.head.pos, argss.flatten) + val superCall = atPos(pos)(PrimarySuperCall(argss)) + deriveDefDef(primaryCtor)(block => Block(earlyVals :+ superCall, unit) setPos pos) setPos pos + case _ => primaryCtor + } + body mapConserve { case `primaryCtor` => primaryCtor1; case stat => stat } + } val body1 = typedStats(body, templ.symbol) @@ -1897,8 +2025,7 @@ trait Typers extends Modes with Adaptations with Tags { val typer1 = constrTyperIf(sym.isParameter && sym.owner.isConstructor) val typedMods = typedModifiers(vdef.mods) - // complete lazy annotations - val annots = sym.annotations + sym.annotations.map(_.completeInfo) var tpt1 = checkNoEscaping.privates(sym, typer1.typedType(vdef.tpt)) checkNonCyclic(vdef, tpt1) @@ -2141,8 +2268,7 @@ trait Typers extends Modes with Adaptations with Tags { val tparams1 = ddef.tparams mapConserve typedTypeDef val vparamss1 = ddef.vparamss mapConserve (_ mapConserve typedValDef) - // complete lazy annotations - val annots = meth.annotations + meth.annotations.map(_.completeInfo) for (vparams1 <- vparamss1; vparam1 <- vparams1 dropRight 1) if (isRepeatedParamType(vparam1.symbol.tpe)) @@ -2217,8 +2343,7 @@ trait Typers extends Modes with Adaptations with Tags { reenterTypeParams(tdef.tparams) val tparams1 = tdef.tparams mapConserve typedTypeDef val typedMods = typedModifiers(tdef.mods) - // complete lazy annotations - val annots = tdef.symbol.annotations + tdef.symbol.annotations.map(_.completeInfo) // @specialized should not be pickled when compiling with -no-specialize if (settings.nospecialization.value && currentRun.compiles(tdef.symbol)) { @@ -2603,7 +2728,7 @@ trait Typers extends Modes with Adaptations with Tags { if (members.head eq EmptyTree) setError(tree) else { val typedBlock = typedPos(tree.pos, mode, pt) { - Block(ClassDef(anonClass, NoMods, ListOfNil, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)(New(anonClass.tpe))) + Block(ClassDef(anonClass, NoMods, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)(New(anonClass.tpe))) } // Don't leak implementation details into the type, see SI-6575 if (isPartial && !typedBlock.isErrorTyped) @@ -2710,6 +2835,7 @@ trait Typers extends Modes with Adaptations with Tags { def typedRefinement(templ: Template) { val stats = templ.body namer.enterSyms(stats) + // need to delay rest of typedRefinement to avoid cyclic reference errors unit.toCheck += { () => val stats1 = typedStats(stats, NoSymbol) @@ -2828,7 +2954,11 @@ trait Typers extends Modes with Adaptations with Tags { var moreToAdd = true while (moreToAdd) { val initElems = scope.elems - for (sym <- scope) + // SI-5877 The decls of a package include decls of the package object. But we don't want to add + // the corresponding synthetics to the package class, only to the package object class. + def shouldAdd(sym: Symbol) = + inBlock || !isInPackageObject(sym, context.owner) + for (sym <- scope if shouldAdd(sym)) for (tree <- context.unit.synthetics get sym) { newStats += typedStat(tree) // might add even more synthetics to the scope context.unit.synthetics -= sym @@ -3367,12 +3497,13 @@ trait Typers extends Modes with Adaptations with Tags { // println(util.Position.formatMessage(uncheckedPattern.pos, "made unchecked type test into a checked one", true)) val args = List(uncheckedPattern) + val app = atPos(uncheckedPattern.pos)(Apply(classTagExtractor, args)) // must call doTypedUnapply directly, as otherwise we get undesirable rewrites // and re-typechecks of the target of the unapply call in PATTERNmode, // this breaks down when the classTagExtractor (which defineds the unapply member) is not a simple reference to an object, // but an arbitrary tree as is the case here - doTypedUnapply(Apply(classTagExtractor, args), classTagExtractor, classTagExtractor, args, PATTERNmode, pt) - } + doTypedUnapply(app, classTagExtractor, classTagExtractor, args, PATTERNmode, pt) + } // if there's a ClassTag that allows us to turn the unchecked type test for `pt` into a checked type test // return the corresponding extractor (an instance of ClassTag[`pt`]) @@ -3891,7 +4022,7 @@ trait Typers extends Modes with Adaptations with Tags { case DynamicApplicationNamed(qual, _) if acceptsApplyDynamic(qual.tpe.widen) => true case _ => false // look deeper? - // val methPart = treeInfo.methPart(fun) + // val treeInfo.Applied(methPart, _, _) = fun // println("methPart of "+ fun +" is "+ methPart) // if (methPart ne fun) isApplyDynamicNamed(methPart) // else false @@ -3927,7 +4058,7 @@ trait Typers extends Modes with Adaptations with Tags { */ def mkInvoke(cxTree: Tree, tree: Tree, qual: Tree, name: Name): Option[Tree] = { log(s"dyna.mkInvoke($cxTree, $tree, $qual, $name)") - val treeSelection = treeInfo.methPart(tree) + val treeInfo.Applied(treeSelection, _, _) = tree def isDesugaredApply = treeSelection match { case Select(`qual`, nme.apply) => true case _ => false @@ -3940,7 +4071,7 @@ trait Typers extends Modes with Adaptations with Tags { // not supported: foo.bar(a1,..., an: _*) def hasStar(args: List[Tree]) = treeInfo.isWildcardStarArgList(args) def applyOp(args: List[Tree]) = if (hasNamed(args)) nme.applyDynamicNamed else nme.applyDynamic - def matches(t: Tree) = isDesugaredApply || treeInfo.methPart(t) == treeSelection + def matches(t: Tree) = isDesugaredApply || treeInfo.dissectApplied(t).core == treeSelection /** Note that the trees which arrive here are potentially some distance from * the trees of direct interest. `cxTree` is some enclosing expression which @@ -3958,9 +4089,8 @@ trait Typers extends Modes with Adaptations with Tags { case _ => t.children flatMap findSelection headOption } findSelection(cxTree) match { - case Some((opName, tapply)) => - val targs = treeInfo.typeArguments(tapply) - val fun = gen.mkTypeApply(Select(qual, opName), targs) + case Some((opName, treeInfo.Applied(_, targs, _))) => + val fun = gen.mkTypeApply(Select(qual, opName), targs) atPos(qual.pos)(Apply(fun, Literal(Constant(name.decode)) :: Nil)) case _ => setError(tree) @@ -4149,8 +4279,8 @@ trait Typers extends Modes with Adaptations with Tags { return fail() if (treeInfo.mayBeVarGetter(varsym)) { - treeInfo.methPart(lhs1) match { - case Select(qual, name) => + lhs1 match { + case treeInfo.Applied(Select(qual, name), _, _) => val sel = Select(qual, nme.getterToSetter(name.toTermName)) setPos lhs.pos val app = Apply(sel, List(rhs)) setPos tree.pos return typed(app, mode, pt) @@ -4517,12 +4647,11 @@ trait Typers extends Modes with Adaptations with Tags { // [Eugene] no more MaxArrayDims. ClassTags are flexible enough to allow creation of arrays of arbitrary dimensionality (w.r.t JVM restrictions) val Some((level, componentType)) = erasure.GenericArray.unapply(tpt.tpe) val tagType = List.iterate(componentType, level)(tpe => appliedType(ArrayClass.toTypeConstructor, List(tpe))).last - val newArrayApp = atPos(tree.pos) { + atPos(tree.pos) { val tag = resolveClassTag(tree.pos, tagType) if (tag.isEmpty) MissingClassTagError(tree, tagType) - else new ApplyToImplicitArgs(Select(tag, nme.newArray), args) + else typed(new ApplyToImplicitArgs(Select(tag, nme.newArray), args)) } - typed(newArrayApp, mode, pt) case Apply(Select(fun, nme.apply), _) if treeInfo.isSuperConstrCall(fun) => //SI-5696 TooManyArgumentListsForConstructor(tree) case tree1 => @@ -4568,9 +4697,9 @@ trait Typers extends Modes with Adaptations with Tags { } case Apply(fn, indices) => - treeInfo.methPart(fn) match { - case Select(table, nme.apply) => mkUpdate(table, indices) - case _ => UnexpectedTreeAssignmentConversionError(qual) + fn match { + case treeInfo.Applied(Select(table, nme.apply), _, _) => mkUpdate(table, indices) + case _ => UnexpectedTreeAssignmentConversionError(qual) } } typed1(tree1, mode, pt) @@ -5027,7 +5156,7 @@ trait Typers extends Modes with Adaptations with Tags { val tree1 = ( if (qual == EmptyTree) tree // atPos necessary because qualifier might come from startContext - else atPos(tree.pos)(Select(qual, name)) + else atPos(tree.pos)(Select(qual, name) setAttachments tree.attachments) ) val (tree2, pre2) = makeAccessible(tree1, defSym, pre, qual) // assert(pre.typeArgs isEmpty) // no need to add #2416-style check here, right? @@ -5052,14 +5181,20 @@ trait Typers extends Modes with Adaptations with Tags { def typedCompoundTypeTree(tree: CompoundTypeTree) = { val templ = tree.templ val parents1 = templ.parents mapConserve (typedType(_, mode)) - if (parents1 exists (_.isErrorTyped)) tree setType ErrorType + + // This is also checked later in typedStats, but that is too late for SI-5361, so + // we eagerly check this here. + for (stat <- templ.body if !treeInfo.isDeclarationOrTypeDef(stat)) + OnlyDeclarationsError(stat) + + if ((parents1 ++ templ.body) exists (_.isErrorTyped)) tree setType ErrorType else { val decls = newScope //Console.println("Owner: " + context.enclClass.owner + " " + context.enclClass.owner.id) val self = refinedType(parents1 map (_.tpe), context.enclClass.owner, decls, templ.pos) newTyper(context.make(templ, self.typeSymbol, decls)).typedRefinement(templ) templ updateAttachment CompoundTypeTreeOriginalAttachment(parents1, Nil) // stats are set elsewhere - tree setType self + tree setType (if (templ.exists(_.isErroneous)) ErrorType else self) // Being conservative to avoid SI-5361 } } @@ -5122,16 +5257,14 @@ trait Typers extends Modes with Adaptations with Tags { def typedPackageDef(pdef: PackageDef) = { val pid1 = typedQualifier(pdef.pid).asInstanceOf[RefTree] assert(sym.moduleClass ne NoSymbol, sym) - // complete lazy annotations - val annots = sym.annotations val stats1 = newTyper(context.make(tree, sym.moduleClass, sym.info.decls)) .typedStats(pdef.stats, NoSymbol) treeCopy.PackageDef(tree, pid1, stats1) setType NoType } def typedDocDef(docdef: DocDef) = { - val comment = docdef.comment if (forScaladoc && (sym ne null) && (sym ne NoSymbol)) { + val comment = docdef.comment docComments(sym) = comment comment.defineVariables(sym) val typer1 = newTyper(context.makeNewScope(tree, context.owner)) @@ -5602,21 +5735,21 @@ trait Typers extends Modes with Adaptations with Tags { // enough to see those. See #3938 ConstructorPrefixError(tree, restpe) } else { - //@M fix for #2208 - // if there are no type arguments, normalization does not bypass any checks, so perform it to get rid of AnyRef - if (result.tpe.typeArgs.isEmpty) { - // minimal check: if(result.tpe.typeSymbolDirect eq AnyRefClass) { - // must expand the fake AnyRef type alias, because bootstrapping (init in Definitions) is not - // designed to deal with the cycles in the scala package (ScalaObject extends - // AnyRef, but the AnyRef type alias is entered after the scala package is - // loaded and completed, so that ScalaObject is unpickled while AnyRef is not - // yet defined ) - // !!! TODO - revisit now that ScalaObject is gone. - result setType(restpe) - } else { // must not normalize: type application must be (bounds-)checked (during RefChecks), see #2208 - // during uncurry (after refchecks), all types are normalized - result - } + //@M fix for #2208 + // if there are no type arguments, normalization does not bypass any checks, so perform it to get rid of AnyRef + if (result.tpe.typeArgs.isEmpty) { + // minimal check: if(result.tpe.typeSymbolDirect eq AnyRefClass) { + // must expand the fake AnyRef type alias, because bootstrapping (init in Definitions) is not + // designed to deal with the cycles in the scala package (ScalaObject extends + // AnyRef, but the AnyRef type alias is entered after the scala package is + // loaded and completed, so that ScalaObject is unpickled while AnyRef is not + // yet defined ) + // !!! TODO - revisit now that ScalaObject is gone. + result setType(restpe) + } else { // must not normalize: type application must be (bounds-)checked (during RefChecks), see #2208 + // during uncurry (after refchecks), all types are normalized + result + } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index bf44b65406..a34d7389bf 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -126,7 +126,7 @@ trait Unapplies extends ast.TreeDSL ModuleDef( Modifiers(cdef.mods.flags & AccessFlags | SYNTHETIC, cdef.mods.privateWithin), cdef.name.toTermName, - Template(parents, emptyValDef, NoMods, Nil, ListOfNil, body, cdef.impl.pos.focus)) + Template(parents, emptyValDef, NoMods, Nil, body, cdef.impl.pos.focus)) } private val caseMods = Modifiers(SYNTHETIC | CASE) diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 95135b84e0..0125f1b189 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -230,7 +230,6 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => emptyValDef, NoMods, List(), - List(List()), List(methdef), NoPosition)) trace("wrapped: ")(showAttributed(moduledef, true, true, settings.Yshowsymkinds.value)) diff --git a/src/eclipse/continuations-library/.classpath b/src/eclipse/continuations-library/.classpath new file mode 100644 index 0000000000..b3ca4eeb48 --- /dev/null +++ b/src/eclipse/continuations-library/.classpath @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="library"/> + <classpathentry combineaccessrules="false" kind="src" path="/scala-library"/> + <classpathentry kind="con" path="org.scala-ide.sdt.launching.SCALA_CONTAINER"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry kind="output" path="build-quick-continuations-library"/> +</classpath> diff --git a/src/eclipse/continuations-library/.project b/src/eclipse/continuations-library/.project new file mode 100644 index 0000000000..f3a53a3d97 --- /dev/null +++ b/src/eclipse/continuations-library/.project @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>continuations-library</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.scala-ide.sdt.core.scalabuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.scala-ide.sdt.core.scalanature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> + <linkedResources> + <link> + <name>build-quick-continuations-library</name> + <type>2</type> + <locationURI>SCALA_BASEDIR/build/quick/classes/continuations/library</locationURI> + </link> + <link> + <name>library</name> + <type>2</type> + <locationURI>SCALA_BASEDIR/src/continuations/library</locationURI> + </link> + </linkedResources> +</projectDescription> diff --git a/src/eclipse/continuations-library/.settings/org.scala-ide.sdt.core.prefs b/src/eclipse/continuations-library/.settings/org.scala-ide.sdt.core.prefs new file mode 100644 index 0000000000..63e1df247f --- /dev/null +++ b/src/eclipse/continuations-library/.settings/org.scala-ide.sdt.core.prefs @@ -0,0 +1,2 @@ +P=continuations\:enable +scala.compiler.useProjectSettings=true diff --git a/src/eclipse/partest/.classpath b/src/eclipse/partest/.classpath index b14e465aa6..7936d4d4b4 100644 --- a/src/eclipse/partest/.classpath +++ b/src/eclipse/partest/.classpath @@ -10,5 +10,6 @@ <classpathentry kind="lib" path="lib/jline.jar"/> <classpathentry kind="lib" path="lib/msil.jar"/> <classpathentry combineaccessrules="false" kind="src" path="/asm"/> + <classpathentry combineaccessrules="false" kind="src" path="/continuations-library"/> <classpathentry kind="output" path="build-quick-partest"/> </classpath> diff --git a/src/eclipse/reflect/.classpath b/src/eclipse/reflect/.classpath index 57a3928dc3..36e6b6adf1 100644 --- a/src/eclipse/reflect/.classpath +++ b/src/eclipse/reflect/.classpath @@ -3,5 +3,6 @@ <classpathentry kind="src" path="reflect"/> <classpathentry combineaccessrules="false" kind="src" path="/scala-library"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/> + <classpathentry combineaccessrules="false" kind="src" path="/continuations-library"/> <classpathentry kind="output" path="build-quick-reflect"/> </classpath> diff --git a/src/eclipse/scala-compiler/.classpath b/src/eclipse/scala-compiler/.classpath index 40a4ed9996..d438d3e610 100644 --- a/src/eclipse/scala-compiler/.classpath +++ b/src/eclipse/scala-compiler/.classpath @@ -9,5 +9,6 @@ <classpathentry kind="lib" path="lib/ant/ant.jar"/> <classpathentry kind="lib" path="lib/jline.jar"/> <classpathentry kind="lib" path="lib/msil.jar"/> + <classpathentry combineaccessrules="false" kind="src" path="/continuations-library"/> <classpathentry kind="output" path="build-quick-compiler"/> </classpath> diff --git a/src/eclipse/scalap/.classpath b/src/eclipse/scalap/.classpath index 2b44ad19b2..16737bd9cd 100644 --- a/src/eclipse/scalap/.classpath +++ b/src/eclipse/scalap/.classpath @@ -8,5 +8,6 @@ <classpathentry kind="lib" path="lib/ant/ant.jar"/> <classpathentry kind="lib" path="lib/jline.jar"/> <classpathentry kind="lib" path="lib/msil.jar"/> + <classpathentry combineaccessrules="false" kind="src" path="/continuations-library"/> <classpathentry kind="output" path="build-quick-scalap"/> </classpath> diff --git a/src/library/scala/StringContext.scala b/src/library/scala/StringContext.scala index 8be0cb1619..1b5fd6c8d3 100644 --- a/src/library/scala/StringContext.scala +++ b/src/library/scala/StringContext.scala @@ -100,8 +100,8 @@ case class StringContext(parts: String*) { * ''Note:'' Even when using the raw interpolator, Scala will preprocess unicode escapes. * For example: * {{{ - * scala> raw"\u0123" - * res0: String = ģ + * scala> raw"\u005cu0025" + * res0: String = # * }}} * * @param `args` The arguments to be inserted into the resulting string. @@ -191,7 +191,7 @@ object StringContext { var cur = 0 var idx = 0 def output(ch: Char) = { - bldr append str.substring (start, cur) + bldr.append(str, start, cur) bldr append ch start = idx } @@ -199,14 +199,15 @@ object StringContext { cur = idx if (str(idx) == '\\') { idx += 1 + if (idx >= len) throw new InvalidEscapeException(str, cur) if ('0' <= str(idx) && str(idx) <= '7') { val leadch = str(idx) var oct = leadch - '0' idx += 1 - if ('0' <= str(idx) && str(idx) <= '7') { + if (idx < len && '0' <= str(idx) && str(idx) <= '7') { oct = oct * 8 + str(idx) - '0' idx += 1 - if (leadch <= '3' && '0' <= str(idx) && str(idx) <= '7') { + if (idx < len && leadch <= '3' && '0' <= str(idx) && str(idx) <= '7') { oct = oct * 8 + str(idx) - '0' idx += 1 } @@ -234,6 +235,6 @@ object StringContext { } } if (start == 0) str - else (bldr append str.substring(start, idx)).toString + else bldr.append(str, start, idx).toString } } diff --git a/src/library/scala/collection/SeqLike.scala b/src/library/scala/collection/SeqLike.scala index 6871a3cb73..f65e2ef9cd 100644 --- a/src/library/scala/collection/SeqLike.scala +++ b/src/library/scala/collection/SeqLike.scala @@ -22,7 +22,7 @@ import scala.math.{ min, max, Ordering } * Sequences are special cases of iterable collections of class `Iterable`. * Unlike iterables, sequences always have a defined order of elements. * Sequences provide a method `apply` for indexing. Indices range from `0` up to the `length` of - * a sequence. Sequences support a number to find occurrences of elements or subsequences, including + * a sequence. Sequences support a number of methods to find occurrences of elements or subsequences, including * `segmentLength`, `prefixLength`, `indexWhere`, `indexOf`, `lastIndexWhere`, `lastIndexOf`, * `startsWith`, `endsWith`, `indexOfSlice`. * diff --git a/src/library/scala/collection/convert/WrapAsJava.scala b/src/library/scala/collection/convert/WrapAsJava.scala index 9115b9e5fb..5e6126a7cf 100644 --- a/src/library/scala/collection/convert/WrapAsJava.scala +++ b/src/library/scala/collection/convert/WrapAsJava.scala @@ -10,10 +10,11 @@ package scala.collection package convert import java.{ lang => jl, util => ju }, java.util.{ concurrent => juc } -import Wrappers._ import scala.language.implicitConversions trait WrapAsJava { + import Wrappers._ + /** * Implicitly converts a Scala Iterator to a Java Iterator. * The returned Java Iterator is backed by the provided Scala diff --git a/src/library/scala/collection/convert/WrapAsScala.scala b/src/library/scala/collection/convert/WrapAsScala.scala index 5a5d204ece..ffcca62291 100644 --- a/src/library/scala/collection/convert/WrapAsScala.scala +++ b/src/library/scala/collection/convert/WrapAsScala.scala @@ -10,11 +10,13 @@ package scala.collection package convert import java.{ lang => jl, util => ju }, java.util.{ concurrent => juc } -import Wrappers._ import scala.language.implicitConversions trait LowPriorityWrapAsScala { this: WrapAsScala => + + import Wrappers._ + /** * Implicitly converts a Java ConcurrentMap to a Scala mutable ConcurrentMap. * The returned Scala ConcurrentMap is backed by the provided Java @@ -34,6 +36,7 @@ trait LowPriorityWrapAsScala { } trait WrapAsScala extends LowPriorityWrapAsScala { + import Wrappers._ /** * Implicitly converts a Java `Iterator` to a Scala `Iterator`. * diff --git a/src/library/scala/collection/immutable/Range.scala b/src/library/scala/collection/immutable/Range.scala index 3b6ab81146..802e16605d 100644 --- a/src/library/scala/collection/immutable/Range.scala +++ b/src/library/scala/collection/immutable/Range.scala @@ -33,7 +33,6 @@ import scala.collection.parallel.immutable.ParRange * @see [[http://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#ranges "Scala's Collection Library overview"]] * section on `Ranges` for more information. * - * @define Coll Range * @define coll range * @define mayNotTerminateInf * @define willNotTerminateInf diff --git a/src/library/scala/collection/mutable/MutableList.scala b/src/library/scala/collection/mutable/MutableList.scala index 183de5b727..fd92d2e555 100644 --- a/src/library/scala/collection/mutable/MutableList.scala +++ b/src/library/scala/collection/mutable/MutableList.scala @@ -56,12 +56,16 @@ extends AbstractSeq[A] /** Returns the rest of this list */ override def tail: MutableList[A] = { - require(nonEmpty, "tail of empty list") val tl = new MutableList[A] + tailImpl(tl) + tl + } + + protected final def tailImpl(tl: MutableList[A]) { + require(nonEmpty, "tail of empty list") tl.first0 = first0.tail - tl.last0 = last0 tl.len = len - 1 - tl + tl.last0 = if (tl.len == 0) tl.first0 else last0 } /** Prepends a single element to this list. This operation takes constant diff --git a/src/library/scala/collection/mutable/Queue.scala b/src/library/scala/collection/mutable/Queue.scala index 5ea012eb9f..b947fa3cca 100644 --- a/src/library/scala/collection/mutable/Queue.scala +++ b/src/library/scala/collection/mutable/Queue.scala @@ -66,7 +66,7 @@ extends MutableList[A] else { val res = first0.elem first0 = first0.next - len -= 1 + decrementLength() res } @@ -82,11 +82,11 @@ extends MutableList[A] else if (p(first0.elem)) { val res: Option[A] = Some(first0.elem) first0 = first0.next - len -= 1 + decrementLength() res } else { val optElem = removeFromList(p) - if (optElem != None) len -= 1 + if (optElem != None) decrementLength() optElem } @@ -119,7 +119,7 @@ extends MutableList[A] while ((first0.nonEmpty) && p(first0.elem)) { res += first0.elem first0 = first0.next - len -= 1 + decrementLength() } if (first0.isEmpty) res else removeAllFromList(p, res) @@ -130,10 +130,10 @@ extends MutableList[A] var leftlst = first0 while (leftlst.next.nonEmpty) { if (p(leftlst.next.elem)) { - res += leftlst.next.elem - if (leftlst.next eq last0) last0 = leftlst - leftlst.next = leftlst.next.next - len -= 1 + res += leftlst.next.elem + if (leftlst.next eq last0) last0 = leftlst + leftlst.next = leftlst.next.next + decrementLength() } else leftlst = leftlst.next } res @@ -154,7 +154,7 @@ extends MutableList[A] else { val res: Option[LinkedList[A]] = Some(cell.next) cell.next = cell.next.next - len -= 1 + decrementLength() res } } @@ -170,11 +170,8 @@ extends MutableList[A] // TODO - Don't override this just for new to create appropriate type.... override def tail: Queue[A] = { - require(nonEmpty, "tail of empty list") val tl = new Queue[A] - tl.first0 = first0.tail - tl.last0 = last0 - tl.len = len - 1 + tailImpl(tl) tl } @@ -183,6 +180,11 @@ extends MutableList[A] bf ++= seq bf.result } + + private[this] def decrementLength() { + len -= 1 + if (len == 0) last0 = first0 + } } diff --git a/src/library/scala/util/Random.scala b/src/library/scala/util/Random.scala index 24c4cd7a32..2b11594f66 100644 --- a/src/library/scala/util/Random.scala +++ b/src/library/scala/util/Random.scala @@ -17,7 +17,7 @@ import scala.language.{implicitConversions, higherKinds} * @author Stephane Micheloud * */ -class Random(val self: java.util.Random) { +class Random(val self: java.util.Random) extends AnyRef with Serializable { /** Creates a new random number generator using a single long seed. */ def this(seed: Long) = this(new java.util.Random(seed)) diff --git a/src/partest/scala/tools/partest/DirectTest.scala b/src/partest/scala/tools/partest/DirectTest.scala index 8c18809ad6..483cb491a1 100644 --- a/src/partest/scala/tools/partest/DirectTest.scala +++ b/src/partest/scala/tools/partest/DirectTest.scala @@ -39,6 +39,10 @@ abstract class DirectTest extends App { // new compiler def newCompiler(args: String*): Global = { val settings = newSettings((CommandLineParser tokenize ("-d \"" + testOutput.path + "\" " + extraSettings)) ++ args.toList) + newCompiler(settings) + } + + def newCompiler(settings: Settings): Global = { if (settings.Yrangepos.value) new Global(settings, reporter(settings)) with interactive.RangePositions else new Global(settings, reporter(settings)) } diff --git a/src/partest/scala/tools/partest/ScaladocModelTest.scala b/src/partest/scala/tools/partest/ScaladocModelTest.scala index e7134d0271..b9abff69d8 100644 --- a/src/partest/scala/tools/partest/ScaladocModelTest.scala +++ b/src/partest/scala/tools/partest/ScaladocModelTest.scala @@ -12,7 +12,7 @@ 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.doc.base.comment._ import scala.tools.nsc.reporters.ConsoleReporter /** A class for testing scaladoc model generation diff --git a/src/reflect/scala/reflect/api/BuildUtils.scala b/src/reflect/scala/reflect/api/BuildUtils.scala index 0c8e81a220..8f256aa1f5 100644 --- a/src/reflect/scala/reflect/api/BuildUtils.scala +++ b/src/reflect/scala/reflect/api/BuildUtils.scala @@ -59,8 +59,6 @@ private[reflect] trait BuildUtils { self: Universe => def flagsFromBits(bits: Long): FlagSet - def emptyValDef: ValDef - def This(sym: Symbol): Tree def Select(qualifier: Tree, sym: Symbol): Select diff --git a/src/reflect/scala/reflect/api/Trees.scala b/src/reflect/scala/reflect/api/Trees.scala index c98107f9b5..cfa6315797 100644 --- a/src/reflect/scala/reflect/api/Trees.scala +++ b/src/reflect/scala/reflect/api/Trees.scala @@ -75,11 +75,26 @@ trait Trees { self: Universe => def isDef: Boolean /** Is this tree one of the empty trees? + * * Empty trees are: the `EmptyTree` null object, `TypeTree` instances that don't carry a type * and the special `emptyValDef` singleton. + * + * In the compiler the `isEmpty` check and the derived `orElse` method are mostly used + * as a check for a tree being a null object (`EmptyTree` for term trees and empty TypeTree for type trees). + * + * Unfortunately `emptyValDef` is also considered to be `isEmpty`, but this is deemed to be + * a conceptual mistake pending a fix in https://issues.scala-lang.org/browse/SI-6762. + * + * @see `canHaveAttrs` */ def isEmpty: Boolean + /** Can this tree carry attributes (i.e. symbols, types or positions)? + * Typically the answer is yes, except for the `EmptyTree` null object and + * two special singletons: `emptyValDef` and `pendingSuperCall`. + */ + def canHaveAttrs: Boolean + /** The canonical way to test if a Tree represents a term. */ def isTerm: Boolean @@ -1611,14 +1626,23 @@ trait Trees { self: Universe => * This node always occurs in the following context: * * (`new` tpt).<init>[targs](args) + * + * For example, an AST representation of: + * + * new Example[Int](2)(3) + * + * is the following code: + * + * Apply( + * Apply( + * TypeApply( + * Select(New(TypeTree(typeOf[Example])), nme.CONSTRUCTOR) + * TypeTree(typeOf[Int])), + * List(Literal(Constant(2)))), + * List(Literal(Constant(3)))) * @group Extractors */ abstract class NewExtractor { - /** A user level `new`. - * One should always use this factory method to build a user level `new`. - * - * @param tpt a class type - */ def apply(tpt: Tree): New def unapply(new_ : New): Option[Tree] } @@ -1720,6 +1744,16 @@ trait Trees { self: Universe => * This AST node corresponds to the following Scala code: * * fun[args] + * + * Should only be used with `fun` nodes which are terms, i.e. which have `isTerm` returning `true`. + * Otherwise `AppliedTypeTree` should be used instead. + * + * def foo[T] = ??? + * foo[Int] // represented as TypeApply(Ident(<foo>), List(TypeTree(<Int>))) + * + * List[Int] as in `val x: List[Int] = ???` + * // represented as AppliedTypeTree(Ident(<List>), List(TypeTree(<Int>))) + * * @group Extractors */ abstract class TypeApplyExtractor { @@ -1890,6 +1924,12 @@ trait Trees { self: Universe => * This AST node corresponds to the following Scala code: * * qualifier.selector + * + * Should only be used with `qualifier` nodes which are terms, i.e. which have `isTerm` returning `true`. + * Otherwise `SelectFromTypeTree` should be used instead. + * + * foo.Bar // represented as Select(Ident(<foo>), <Bar>) + * Foo#Bar // represented as SelectFromTypeTree(Ident(<Foo>), <Bar>) * @group Extractors */ abstract class SelectExtractor { @@ -2122,7 +2162,6 @@ trait Trees { self: Universe => * @group Trees * @template */ - // [Eugene++] don't see why we need it, when we have Select type SelectFromTypeTree >: Null <: TypTree with RefTree with SelectFromTypeTreeApi /** A tag that preserves the identity of the `SelectFromTypeTree` abstract type from erasure. @@ -2142,6 +2181,12 @@ trait Trees { self: Universe => * qualifier # selector * * Note: a path-dependent type p.T is expressed as p.type # T + * + * Should only be used with `qualifier` nodes which are types, i.e. which have `isType` returning `true`. + * Otherwise `Select` should be used instead. + * + * Foo#Bar // represented as SelectFromTypeTree(Ident(<Foo>), <Bar>) + * foo.Bar // represented as Select(Ident(<foo>), <Bar>) * @group Extractors */ abstract class SelectFromTypeTreeExtractor { @@ -2217,6 +2262,15 @@ trait Trees { self: Universe => * This AST node corresponds to the following Scala code: * * tpt[args] + * + * Should only be used with `tpt` nodes which are types, i.e. which have `isType` returning `true`. + * Otherwise `TypeApply` should be used instead. + * + * List[Int] as in `val x: List[Int] = ???` + * // represented as AppliedTypeTree(Ident(<List>), List(TypeTree(<Int>))) + * + * def foo[T] = ??? + * foo[Int] // represented as TypeApply(Ident(<foo>), List(TypeTree(<Int>))) * @group Extractors */ abstract class AppliedTypeTreeExtractor { @@ -2366,123 +2420,155 @@ trait Trees { self: Universe => */ val emptyValDef: ValDef + /** An empty superclass constructor call corresponding to: + * super.<init>() + * This is used as a placeholder in the primary constructor body in class templates + * to denote the insertion point of a call to superclass constructor after the typechecker + * figures out the superclass of a given template. + * @group Trees + */ + val pendingSuperCall: Apply + // ---------------------- factories ---------------------------------------------- /** A factory method for `ClassDef` nodes. * @group Factories */ + @deprecated("Use the canonical ClassDef constructor to create a class and then initialize its position and symbol manually", "2.10.1") def ClassDef(sym: Symbol, impl: Template): ClassDef /** A factory method for `ModuleDef` nodes. * @group Factories */ + @deprecated("Use the canonical ModuleDef constructor to create an object and then initialize its position and symbol manually", "2.10.1") def ModuleDef(sym: Symbol, impl: Template): ModuleDef /** A factory method for `ValDef` nodes. * @group Factories */ + @deprecated("Use the canonical ValDef constructor to create a val and then initialize its position and symbol manually", "2.10.1") def ValDef(sym: Symbol, rhs: Tree): ValDef /** A factory method for `ValDef` nodes. * @group Factories */ + @deprecated("Use the canonical ValDef constructor to create a val with an empty right-hand side and then initialize its position and symbol manually", "2.10.1") def ValDef(sym: Symbol): ValDef /** A factory method for `ValDef` nodes. * @group Factories */ + @deprecated("Use the canonical DefDef constructor to create a method and then initialize its position and symbol manually", "2.10.1") def DefDef(sym: Symbol, mods: Modifiers, vparamss: List[List[ValDef]], rhs: Tree): DefDef /** A factory method for `ValDef` nodes. * @group Factories */ + @deprecated("Use the canonical DefDef constructor to create a method and then initialize its position and symbol manually", "2.10.1") def DefDef(sym: Symbol, vparamss: List[List[ValDef]], rhs: Tree): DefDef /** A factory method for `ValDef` nodes. * @group Factories */ + @deprecated("Use the canonical DefDef constructor to create a method and then initialize its position and symbol manually", "2.10.1") def DefDef(sym: Symbol, mods: Modifiers, rhs: Tree): DefDef /** A factory method for `ValDef` nodes. * @group Factories */ + @deprecated("Use the canonical DefDef constructor to create a method and then initialize its position and symbol manually", "2.10.1") def DefDef(sym: Symbol, rhs: Tree): DefDef /** A factory method for `ValDef` nodes. * @group Factories */ + @deprecated("Use the canonical DefDef constructor to create a method and then initialize its position and symbol manually", "2.10.1") def DefDef(sym: Symbol, rhs: List[List[Symbol]] => Tree): DefDef /** A factory method for `TypeDef` nodes. * @group Factories */ + @deprecated("Use the canonical TypeDef constructor to create a type alias and then initialize its position and symbol manually", "2.10.1") def TypeDef(sym: Symbol, rhs: Tree): TypeDef /** A factory method for `TypeDef` nodes. * @group Factories */ + @deprecated("Use the canonical TypeDef constructor to create an abstract type or type parameter and then initialize its position and symbol manually", "2.10.1") def TypeDef(sym: Symbol): TypeDef /** A factory method for `LabelDef` nodes. * @group Factories */ + @deprecated("Use the canonical LabelDef constructor to create a label and then initialize its position and symbol manually", "2.10.1") def LabelDef(sym: Symbol, params: List[Symbol], rhs: Tree): LabelDef /** A factory method for `Block` nodes. * Flattens directly nested blocks. * @group Factories */ + @deprecated("Use the canonical Block constructor, explicitly specifying its expression if necessary. Flatten directly nested blocks manually if needed", "2.10.1") def Block(stats: Tree*): Block /** A factory method for `CaseDef` nodes. * @group Factories */ + @deprecated("Use the canonical CaseDef constructor passing EmptyTree for guard", "2.10.1") def CaseDef(pat: Tree, body: Tree): CaseDef /** A factory method for `Bind` nodes. * @group Factories */ + @deprecated("Use the canonical Bind constructor to create a bind and then initialize its symbol manually", "2.10.1") def Bind(sym: Symbol, body: Tree): Bind /** A factory method for `Try` nodes. * @group Factories */ + @deprecated("Use canonical CaseDef constructors to to create exception catching expressions and then wrap them in Try", "2.10.1") def Try(body: Tree, cases: (Tree, Tree)*): Try /** A factory method for `Throw` nodes. * @group Factories */ + @deprecated("Use the canonical New constructor to create an object instantiation expression and then wrap it in Throw", "2.10.1") def Throw(tpe: Type, args: Tree*): Throw /** Factory method for object creation `new tpt(args_1)...(args_n)` * A `New(t, as)` is expanded to: `(new t).<init>(as)` * @group Factories */ + @deprecated("Use Apply(...Apply(Select(New(tpt), nme.CONSTRUCTOR), args1)...argsN) instead", "2.10.1") def New(tpt: Tree, argss: List[List[Tree]]): Tree /** 0-1 argument list new, based on a type. * @group Factories */ + @deprecated("Use New(TypeTree(tpe), args.toList) instead", "2.10.1") def New(tpe: Type, args: Tree*): Tree /** 0-1 argument list new, based on a symbol. * @group Factories */ + @deprecated("Use New(sym.toType, args) instead", "2.10.1") def New(sym: Symbol, args: Tree*): Tree /** A factory method for `Apply` nodes. * @group Factories */ + @deprecated("Use Apply(Ident(sym), args.toList) instead", "2.10.1") def Apply(sym: Symbol, args: Tree*): Tree /** 0-1 argument list new, based on a type tree. * @group Factories */ + @deprecated("Use Apply(Select(New(tpt), nme.CONSTRUCTOR), args) instead", "2.10.1") def ApplyConstructor(tpt: Tree, args: List[Tree]): Tree /** A factory method for `Super` nodes. * @group Factories */ + @deprecated("Use Super(This(sym), mix) instead", "2.10.1") def Super(sym: Symbol, mix: TypeName): Tree /** A factory method for `This` nodes. @@ -2494,6 +2580,7 @@ trait Trees { self: Universe => * The string `name` argument is assumed to represent a [[scala.reflect.api.Names#TermName `TermName`]]. * @group Factories */ + @deprecated("Use Select(tree, newTermName(name)) instead", "2.10.1") def Select(qualifier: Tree, name: String): Select /** A factory method for `Select` nodes. @@ -2504,6 +2591,7 @@ trait Trees { self: Universe => /** A factory method for `Ident` nodes. * @group Factories */ + @deprecated("Use Ident(newTermName(name)) instead", "2.10.1") def Ident(name: String): Ident /** A factory method for `Ident` nodes. @@ -2843,7 +2931,8 @@ trait Trees { self: Universe => trees mapConserve (tree => transform(tree).asInstanceOf[TypeDef]) /** Transforms a `ValDef`. */ def transformValDef(tree: ValDef): ValDef = - if (tree.isEmpty) tree else transform(tree).asInstanceOf[ValDef] + if (tree eq emptyValDef) tree + else transform(tree).asInstanceOf[ValDef] /** Transforms a list of `ValDef` nodes. */ def transformValDefs(trees: List[ValDef]): List[ValDef] = trees mapConserve (transformValDef(_)) diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala index 7c12b5979d..6a5a742cc7 100644 --- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -201,6 +201,8 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => override def toString = if (forced) forcedInfo.toString else "@<?>" override def pos: Position = if (forced) forcedInfo.pos else NoPosition + + override def completeInfo(): Unit = forcedInfo } /** Typed information about an annotation. It can be attached to either @@ -242,6 +244,9 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => this } + // Forces LazyAnnotationInfo, no op otherwise + def completeInfo(): Unit = () + /** Annotations annotating annotations are confusing so I drew * an example. Given the following code: * diff --git a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala index d72f08674e..3c2b128c52 100644 --- a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala +++ b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala @@ -60,7 +60,7 @@ trait BaseTypeSeqs { elems(i) match { case rtp @ RefinedType(variants, decls) => // can't assert decls.isEmpty; see t0764 - //if (!decls.isEmpty) assert(false, "computing closure of "+this+":"+this.isInstanceOf[RefinedType]+"/"+closureCache(j)) + //if (!decls.isEmpty) abort("computing closure of "+this+":"+this.isInstanceOf[RefinedType]+"/"+closureCache(j)) //Console.println("compute closure of "+this+" => glb("+variants+")") pending += i try { diff --git a/src/reflect/scala/reflect/internal/BuildUtils.scala b/src/reflect/scala/reflect/internal/BuildUtils.scala index 9f41f0336e..b1b0c5b60b 100644 --- a/src/reflect/scala/reflect/internal/BuildUtils.scala +++ b/src/reflect/scala/reflect/internal/BuildUtils.scala @@ -47,8 +47,6 @@ trait BuildUtils { self: SymbolTable => def flagsFromBits(bits: Long): FlagSet = bits - def emptyValDef: ValDef = self.emptyValDef - def This(sym: Symbol): Tree = self.This(sym) def Select(qualifier: Tree, sym: Symbol): Select = self.Select(qualifier, sym) diff --git a/src/reflect/scala/reflect/internal/Flags.scala b/src/reflect/scala/reflect/internal/Flags.scala index 30dd9c3e49..a0362c8921 100644 --- a/src/reflect/scala/reflect/internal/Flags.scala +++ b/src/reflect/scala/reflect/internal/Flags.scala @@ -262,7 +262,7 @@ class Flags extends ModifierFlags { * Getters of immutable values also get STABLE. */ final val GetterFlags = ~(PRESUPER | MUTABLE) - final val SetterFlags = ~(PRESUPER | MUTABLE | STABLE | CASEACCESSOR) + final val SetterFlags = ~(PRESUPER | MUTABLE | STABLE | CASEACCESSOR | IMPLICIT) /** When a symbol for a default getter is created, it inherits these * flags from the method with the default. Other flags applied at creation diff --git a/src/reflect/scala/reflect/internal/Importers.scala b/src/reflect/scala/reflect/internal/Importers.scala index 43902c1930..2f2b02975c 100644 --- a/src/reflect/scala/reflect/internal/Importers.scala +++ b/src/reflect/scala/reflect/internal/Importers.scala @@ -334,6 +334,8 @@ trait Importers extends api.Importers { self: SymbolTable => new ModuleDef(importModifiers(mods), importName(name).toTermName, importTemplate(impl)) case from.emptyValDef => emptyValDef + case from.pendingSuperCall => + pendingSuperCall case from.ValDef(mods, name, tpt, rhs) => new ValDef(importModifiers(mods), importName(name).toTermName, importTree(tpt), importTree(rhs)) case from.DefDef(mods, name, tparams, vparamss, tpt, rhs) => diff --git a/src/reflect/scala/reflect/internal/Positions.scala b/src/reflect/scala/reflect/internal/Positions.scala index faa161d6b1..f8c670827a 100644 --- a/src/reflect/scala/reflect/internal/Positions.scala +++ b/src/reflect/scala/reflect/internal/Positions.scala @@ -38,7 +38,7 @@ trait Positions extends api.Positions { self: SymbolTable => protected class DefaultPosAssigner extends PosAssigner { var pos: Position = _ override def traverse(t: Tree) { - if (t eq EmptyTree) () + if (!t.canHaveAttrs) () else if (t.pos == NoPosition) { t.setPos(pos) super.traverse(t) // TODO: bug? shouldn't the traverse be outside of the if? diff --git a/src/reflect/scala/reflect/internal/Printers.scala b/src/reflect/scala/reflect/internal/Printers.scala index 80d247c0ea..a8085a4c58 100644 --- a/src/reflect/scala/reflect/internal/Printers.scala +++ b/src/reflect/scala/reflect/internal/Printers.scala @@ -435,7 +435,7 @@ trait Printers extends api.Printers { self: SymbolTable => case tree => xprintTree(this, tree) } - if (printTypes && tree.isTerm && !tree.isEmpty) { + if (printTypes && tree.isTerm && tree.canHaveAttrs) { print("{", if (tree.tpe eq null) "<null>" else tree.tpe.toString, "}") } } @@ -542,8 +542,10 @@ trait Printers extends api.Printers { self: SymbolTable => print(")") case EmptyTree => print("EmptyTree") - case emptyValDef: AnyRef if emptyValDef eq self.emptyValDef => + case self.emptyValDef => print("emptyValDef") + case self.pendingSuperCall => + print("pendingSuperCall") case tree: Tree => val hasSymbol = tree.hasSymbol && tree.symbol != NoSymbol val isError = hasSymbol && tree.symbol.name.toString == nme.ERROR.toString diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala index 1df91a67b0..b782353ed3 100644 --- a/src/reflect/scala/reflect/internal/StdAttachments.scala +++ b/src/reflect/scala/reflect/internal/StdAttachments.scala @@ -10,6 +10,7 @@ trait StdAttachments { trait Attachable { protected var rawatt: scala.reflect.macros.Attachments { type Pos = Position } = NoPosition def attachments = rawatt + def setAttachments(attachments: scala.reflect.macros.Attachments { type Pos = Position }): this.type = { rawatt = attachments; this } def updateAttachment[T: ClassTag](attachment: T): this.type = { rawatt = rawatt.update(attachment); this } def removeAttachment[T: ClassTag]: this.type = { rawatt = rawatt.remove[T]; this } diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 5e7f5777b2..c870d8972d 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -730,6 +730,7 @@ trait StdNames { val null_ : NameType = "null" val ofDim: NameType = "ofDim" val origin: NameType = "origin" + val pendingSuperCall: NameType = "pendingSuperCall" val prefix : NameType = "prefix" val productArity: NameType = "productArity" val productElement: NameType = "productElement" diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index a27afe9dfd..a4287fb181 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -3090,7 +3090,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => class RefinementClassSymbol protected[Symbols] (owner0: Symbol, pos0: Position) extends ClassSymbol(owner0, pos0, tpnme.REFINE_CLASS_NAME) { override def name_=(name: Name) { - assert(false, "Cannot set name of RefinementClassSymbol to " + name) + abort("Cannot set name of RefinementClassSymbol to " + name) super.name_=(name) } override def isRefinementClass = true diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index 8ad15f37e4..8908036442 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -159,7 +159,7 @@ abstract class TreeInfo { * Also accounts for varargs. */ private def applyMethodParameters(fn: Tree): List[Symbol] = { - val depth = applyDepth(fn) + val depth = dissectApplied(fn).applyDepth // There could be applies which go beyond the parameter list(s), // being applied to the result of the method call. // !!! Note that this still doesn't seem correct, although it should @@ -195,29 +195,26 @@ abstract class TreeInfo { def isGetter = mayBeVarGetter(sym) && sym.owner.info.member(nme.getterToSetter(sym.name.toTermName)) != NoSymbol tree match { - case Ident(_) => isVar - case Select(_, _) => isVar || isGetter - case _ => - methPart(tree) match { - case Select(qual, nme.apply) => qual.tpe.member(nme.update) != NoSymbol - case _ => false - } + case Ident(_) => isVar + case Select(_, _) => isVar || isGetter + case Applied(Select(qual, nme.apply), _, _) => qual.tpe.member(nme.update) != NoSymbol + case _ => false } } /** Is tree a self constructor call this(...)? I.e. a call to a constructor of the * same object? */ - def isSelfConstrCall(tree: Tree): Boolean = methPart(tree) match { - case Ident(nme.CONSTRUCTOR) - | Select(This(_), nme.CONSTRUCTOR) => true + def isSelfConstrCall(tree: Tree): Boolean = tree match { + case Applied(Ident(nme.CONSTRUCTOR), _, _) => true + case Applied(Select(This(_), nme.CONSTRUCTOR), _, _) => true case _ => false } /** Is tree a super constructor call? */ - def isSuperConstrCall(tree: Tree): Boolean = methPart(tree) match { - case Select(Super(_, _), nme.CONSTRUCTOR) => true + def isSuperConstrCall(tree: Tree): Boolean = tree match { + case Applied(Select(Super(_, _), nme.CONSTRUCTOR), _, _) => true case _ => false } @@ -333,6 +330,9 @@ abstract class TreeInfo { def preSuperFields(stats: List[Tree]): List[ValDef] = stats collect { case vd: ValDef if isEarlyValDef(vd) => vd } + def hasUntypedPreSuperFields(stats: List[Tree]): Boolean = + preSuperFields(stats) exists (_.tpt.isEmpty) + def isEarlyDef(tree: Tree) = tree match { case TypeDef(mods, _, _, _) => mods hasFlag PRESUPER case ValDef(mods, _, _, _) => mods hasFlag PRESUPER @@ -399,22 +399,6 @@ abstract class TreeInfo { case _ => false } - /** If this tree represents a type application (after unwrapping - * any applies) the first type argument. Otherwise, EmptyTree. - */ - def firstTypeArg(tree: Tree): Tree = tree match { - case Apply(fn, _) => firstTypeArg(fn) - case TypeApply(_, targ :: _) => targ - case _ => EmptyTree - } - - /** If this tree represents a type application the type arguments. Otherwise Nil. - */ - def typeArguments(tree: Tree): List[Tree] = tree match { - case TypeApply(_, targs) => targs - case _ => Nil - } - /** If this tree has type parameters, those. Otherwise Nil. */ def typeParameters(tree: Tree): List[TypeDef] = tree match { @@ -513,31 +497,126 @@ abstract class TreeInfo { def isSynthCaseSymbol(sym: Symbol) = sym hasAllFlags SYNTH_CASE_FLAGS def hasSynthCaseSymbol(t: Tree) = t.symbol != null && isSynthCaseSymbol(t.symbol) + def isTraitRef(tree: Tree): Boolean = { + val sym = if (tree.tpe != null) tree.tpe.typeSymbol else null + ((sym ne null) && sym.initialize.isTrait) + } - /** The method part of an application node + /** Applications in Scala can have one of the following shapes: + * + * 1) naked core: Ident(_) or Select(_, _) or basically anything else + * 2) naked core with targs: TypeApply(core, targs) or AppliedTypeTree(core, targs) + * 3) apply or several applies wrapping a core: Apply(core, _), or Apply(Apply(core, _), _), etc + * + * This class provides different ways to decompose applications and simplifies their analysis. + * + * ***Examples*** + * (TypeApply in the examples can be replaced with AppliedTypeTree) + * + * Ident(foo): + * * callee = Ident(foo) + * * core = Ident(foo) + * * targs = Nil + * * argss = Nil + * + * TypeApply(foo, List(targ1, targ2...)) + * * callee = TypeApply(foo, List(targ1, targ2...)) + * * core = foo + * * targs = List(targ1, targ2...) + * * argss = Nil + * + * Apply(foo, List(arg1, arg2...)) + * * callee = foo + * * core = foo + * * targs = Nil + * * argss = List(List(arg1, arg2...)) + * + * Apply(Apply(foo, List(arg21, arg22, ...)), List(arg11, arg12...)) + * * callee = foo + * * core = foo + * * targs = Nil + * * argss = List(List(arg11, arg12...), List(arg21, arg22, ...)) + * + * Apply(Apply(TypeApply(foo, List(targs1, targs2, ...)), List(arg21, arg22, ...)), List(arg11, arg12...)) + * * callee = TypeApply(foo, List(targs1, targs2, ...)) + * * core = foo + * * targs = Nil + * * argss = List(List(arg11, arg12...), List(arg21, arg22, ...)) */ - def methPart(tree: Tree): Tree = tree match { - case Apply(fn, _) => methPart(fn) - case TypeApply(fn, _) => methPart(fn) - case AppliedTypeTree(fn, _) => methPart(fn) - case _ => tree + class Applied(val tree: Tree) { + /** The tree stripped of the possibly nested applications. + * The original tree if it's not an application. + */ + def callee: Tree = { + def loop(tree: Tree): Tree = tree match { + case Apply(fn, _) => loop(fn) + case tree => tree + } + loop(tree) + } + + /** The `callee` unwrapped from type applications. + * The original `callee` if it's not a type application. + */ + def core: Tree = callee match { + case TypeApply(fn, _) => fn + case AppliedTypeTree(fn, _) => fn + case tree => tree + } + + /** The type arguments of the `callee`. + * `Nil` if the `callee` is not a type application. + */ + def targs: List[Tree] = callee match { + case TypeApply(_, args) => args + case AppliedTypeTree(_, args) => args + case _ => Nil + } + + /** (Possibly multiple lists of) value arguments of an application. + * `Nil` if the `callee` is not an application. + */ + def argss: List[List[Tree]] = { + def loop(tree: Tree): List[List[Tree]] = tree match { + case Apply(fn, args) => loop(fn) :+ args + case _ => Nil + } + loop(tree) + } + + /** The depth of the nested applies: e.g. Apply(Apply(Apply(_, _), _), _) + * has depth 3. Continues through type applications (without counting them.) + */ + def applyDepth: Int = { + def loop(tree: Tree): Int = tree match { + case Apply(fn, _) => 1 + loop(fn) + case TypeApply(fn, _) => loop(fn) + case AppliedTypeTree(fn, _) => loop(fn) + case _ => 0 + } + loop(tree) + } } - /** The depth of the nested applies: e.g. Apply(Apply(Apply(_, _), _), _) - * has depth 3. Continues through type applications (without counting them.) + /** Returns a wrapper that knows how to destructure and analyze applications. */ - def applyDepth(tree: Tree): Int = tree match { - case Apply(fn, _) => 1 + applyDepth(fn) - case TypeApply(fn, _) => applyDepth(fn) - case AppliedTypeTree(fn, _) => applyDepth(fn) - case _ => 0 - } - def firstArgument(tree: Tree): Tree = tree match { - case Apply(fn, args) => - val f = firstArgument(fn) - if (f == EmptyTree && !args.isEmpty) args.head else f - case _ => - EmptyTree + def dissectApplied(tree: Tree) = new Applied(tree) + + /** Destructures applications into important subparts described in `Applied` class, + * namely into: core, targs and argss (in the specified order). + * + * Trees which are not applications are also accepted. Their callee and core will + * be equal to the input, while targs and argss will be Nil. + * + * The provided extractors don't expose all the API of the `Applied` class. + * For advanced use, call `dissectApplied` explicitly and use its methods instead of pattern matching. + */ + object Applied { + def unapply(applied: Applied): Option[(Tree, List[Tree], List[List[Tree]])] = + Some((applied.core, applied.targs, applied.argss)) + + def unapply(tree: Tree): Option[(Tree, List[Tree], List[List[Tree]])] = + unapply(dissectApplied(tree)) } /** Does list of trees start with a definition of @@ -634,7 +713,7 @@ abstract class TreeInfo { } def unapply(tree: Tree) = refPart(tree) match { - case ref: RefTree => Some((ref.qualifier.symbol, ref.symbol, typeArguments(tree))) + case ref: RefTree => Some((ref.qualifier.symbol, ref.symbol, dissectApplied(tree).targs)) case _ => None } } diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 6df4b75a88..431afd286d 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -36,6 +36,7 @@ trait Trees extends api.Trees { self: SymbolTable => def isDef = false def isEmpty = false + def canHaveAttrs = true /** The canonical way to test if a Tree represents a term. */ @@ -228,14 +229,6 @@ trait Trees extends api.Trees { self: SymbolTable => override def isDef = true } - case object EmptyTree extends TermTree { - val asList = List(this) - super.tpe_=(NoType) - override def tpe_=(t: Type) = - if (t != NoType) throw new UnsupportedOperationException("tpe_=("+t+") inapplicable for <empty>") - override def isEmpty = true - } - abstract class MemberDef extends DefTree with MemberDefApi { def mods: Modifiers def keyword: String = this match { @@ -599,6 +592,7 @@ trait Trees extends api.Trees { self: SymbolTable => case _: ApplyToImplicitArgs => new ApplyToImplicitArgs(fun, args) case _: ApplyImplicitView => new ApplyImplicitView(fun, args) // TODO: ApplyConstructor ??? + case self.pendingSuperCall => self.pendingSuperCall case _ => new Apply(fun, args) }).copyAttrs(tree) def ApplyDynamic(tree: Tree, qual: Tree, args: List[Tree]) = @@ -817,7 +811,7 @@ trait Trees extends api.Trees { self: SymbolTable => } def Annotated(tree: Tree, annot: Tree, arg: Tree) = tree match { case t @ Annotated(annot0, arg0) - if (annot0==annot) => t + if (annot0==annot && arg0==arg) => t case _ => treeCopy.Annotated(tree, annot, arg) } def SingletonTypeTree(tree: Tree, ref: Tree) = tree match { @@ -961,12 +955,23 @@ trait Trees extends api.Trees { self: SymbolTable => def ValDef(sym: Symbol): ValDef = ValDef(sym, EmptyTree) - object emptyValDef extends ValDef(Modifiers(PRIVATE), nme.WILDCARD, TypeTree(NoType), EmptyTree) { - override def isEmpty = true + trait CannotHaveAttrs extends Tree { + override def canHaveAttrs = false + + private def unsupported(what: String, args: Any*) = + throw new UnsupportedOperationException(s"$what($args) inapplicable for "+self.toString) + super.setPos(NoPosition) - override def setPos(pos: Position) = { assert(false); this } + override def setPos(pos: Position) = unsupported("setPos", pos) + + super.setType(NoType) + override def tpe_=(t: Type) = if (t != NoType) unsupported("tpe_=", t) } + case object EmptyTree extends TermTree with CannotHaveAttrs { override def isEmpty = true; val asList = List(this) } + object emptyValDef extends ValDef(Modifiers(PRIVATE), nme.WILDCARD, TypeTree(NoType), EmptyTree) with CannotHaveAttrs + object pendingSuperCall extends Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List()) with CannotHaveAttrs + def DefDef(sym: Symbol, mods: Modifiers, vparamss: List[List[ValDef]], rhs: Tree): DefDef = atPos(sym.pos) { assert(sym != NoSymbol) @@ -1034,6 +1039,9 @@ trait Trees extends api.Trees { self: SymbolTable => def New(tpe: Type, args: Tree*): Tree = ApplyConstructor(TypeTree(tpe), args.toList) + def New(tpe: Type, argss: List[List[Tree]]): Tree = + New(TypeTree(tpe), argss) + def New(sym: Symbol, args: Tree*): Tree = New(sym.tpe, args: _*) @@ -1114,7 +1122,7 @@ trait Trees extends api.Trees { self: SymbolTable => traverse(annot); traverse(arg) case Template(parents, self, body) => traverseTrees(parents) - if (!self.isEmpty) traverse(self) + if (self ne emptyValDef) traverse(self) traverseStats(body, tree.symbol) case Block(stats, expr) => traverseTrees(stats); traverse(expr) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 0c4cda8313..119a57d268 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -1401,7 +1401,7 @@ trait Types extends api.Types { self: SymbolTable => if (!sym.isClass) { // SI-6640 allow StubSymbols to reveal what's missing from the classpath before we trip the assertion. sym.failIfStub() - assert(false, sym) + abort(s"ThisType($sym) for sym which is not a class") } //assert(sym.isClass && !sym.isModuleClass || sym.isRoot, sym) @@ -6605,11 +6605,11 @@ trait Types extends api.Types { self: SymbolTable => case ExistentialType(qs, _) => qs case t => List() } - def stripType(tp: Type) = tp match { + def stripType(tp: Type): Type = tp match { case ExistentialType(_, res) => res case tv@TypeVar(_, constr) => - if (tv.instValid) constr.inst + if (tv.instValid) stripType(constr.inst) else if (tv.untouchable) tv else abort("trying to do lub/glb of typevar "+tp) case t => t @@ -7067,7 +7067,7 @@ trait Types extends api.Types { self: SymbolTable => case ExistentialType(tparams, quantified) :: rest => mergePrefixAndArgs(quantified :: rest, variance, depth) map (existentialAbstraction(tparams, _)) case _ => - assert(false, tps); None + abort(s"mergePrefixAndArgs($tps, $variance, $depth): unsupported tps") } def addMember(thistp: Type, tp: Type, sym: Symbol): Unit = addMember(thistp, tp, sym, AnyDepth) diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala index f3a5053a91..603fff4f1c 100644 --- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala +++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala @@ -309,7 +309,7 @@ abstract class UnPickler { if (isModuleRoot) moduleRoot setFlag pflags else owner.newLinkedModule(clazz, pflags) case VALsym => - if (isModuleRoot) { assert(false); NoSymbol } + if (isModuleRoot) { abort(s"VALsym at module root: owner = $owner, name = $name") } else owner.newTermSymbol(name.toTermName, NoPosition, pflags) case _ => diff --git a/src/reflect/scala/reflect/internal/transform/UnCurry.scala b/src/reflect/scala/reflect/internal/transform/UnCurry.scala index 0c1640ceb9..6dc6a0f7b8 100644 --- a/src/reflect/scala/reflect/internal/transform/UnCurry.scala +++ b/src/reflect/scala/reflect/internal/transform/UnCurry.scala @@ -19,8 +19,7 @@ trait UnCurry { case MethodType(params, MethodType(params1, restpe)) => apply(MethodType(params ::: params1, restpe)) case MethodType(params, ExistentialType(tparams, restpe @ MethodType(_, _))) => - assert(false, "unexpected curried method types with intervening existential") - tp0 + abort("unexpected curried method types with intervening existential") case MethodType(h :: t, restpe) if h.isImplicit => apply(MethodType(h.cloneSymbol.resetFlag(IMPLICIT) :: t, restpe)) case NullaryMethodType(restpe) => diff --git a/src/reflect/scala/reflect/internal/util/Position.scala b/src/reflect/scala/reflect/internal/util/Position.scala index 0725e9775b..3d10d4c9ce 100644 --- a/src/reflect/scala/reflect/internal/util/Position.scala +++ b/src/reflect/scala/reflect/internal/util/Position.scala @@ -7,6 +7,7 @@ package scala.reflect.internal.util import scala.reflect.ClassTag +import scala.reflect.internal.FatalError import scala.reflect.macros.Attachments object Position { @@ -269,7 +270,7 @@ class OffsetPosition(override val source: SourceFile, override val point: Int) e /** new for position ranges */ class RangePosition(source: SourceFile, override val start: Int, point: Int, override val end: Int) extends OffsetPosition(source, point) { - if (start > end) assert(false, "bad position: "+show) + if (start > end) sys.error("bad position: "+show) override def isRange: Boolean = true override def isOpaqueRange: Boolean = true override def startOrPoint: Int = start diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index ab93d7033a..01e0634902 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -416,7 +416,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni case sym if sym.owner.isPrimitiveValueClass => invokePrimitiveMethod case sym if sym == Predef_classOf => fail("Predef.classOf is a compile-time function") case sym if sym.isTermMacro => fail(s"${symbol.fullName} is a macro, i.e. a compile-time function") - case _ => assert(false, this) + case _ => abort(s"unsupported symbol $symbol when invoking $this") } } } @@ -574,7 +574,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni case None => // class does not have a Scala signature; it's a Java class info("translating reflection info for Java " + jclazz) //debug - initClassModule(clazz, module, new FromJavaClassCompleter(clazz, module, jclazz)) + initClassAndModule(clazz, module, new FromJavaClassCompleter(clazz, module, jclazz)) } } } catch { @@ -676,9 +676,9 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni def enter(sym: Symbol, mods: Int) = (if (jModifier.isStatic(mods)) module.moduleClass else clazz).info.decls enter sym - for (jinner <- jclazz.getDeclaredClasses) { - enter(jclassAsScala(jinner, clazz), jinner.getModifiers) - } + for (jinner <- jclazz.getDeclaredClasses) + jclassAsScala(jinner) // inner class is entered as a side-effect + // no need to call enter explicitly pendingLoadActions = { () => @@ -1036,14 +1036,14 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni * @param jclazz The Java class * @return A Scala class symbol that wraps all reflection info of `jclazz` */ - private def jclassAsScala(jclazz: jClass[_]): Symbol = jclassAsScala(jclazz, sOwner(jclazz)) + private def jclassAsScala(jclazz: jClass[_]): ClassSymbol = + toScala(classCache, jclazz)(_ jclassAsScala1 _) - private def jclassAsScala(jclazz: jClass[_], owner: Symbol): ClassSymbol = { + private def jclassAsScala1(jclazz: jClass[_]): ClassSymbol = { + val owner = sOwner(jclazz) val name = scalaSimpleName(jclazz) val completer = (clazz: Symbol, module: Symbol) => new FromJavaClassCompleter(clazz, module, jclazz) - val (clazz, module) = createClassModule(owner, name, completer) - classCache enter (jclazz, clazz) - clazz + initAndEnterClassAndModule(owner, name, completer)._1 } /** diff --git a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala index 61663f6181..311db64b91 100644 --- a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala +++ b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala @@ -57,7 +57,7 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => * @param name The simple name of the newly created class * @param completer The completer to be used to set the info of the class and the module */ - protected def createClassModule(owner: Symbol, name: TypeName, completer: (Symbol, Symbol) => LazyType) = { + protected def initAndEnterClassAndModule(owner: Symbol, name: TypeName, completer: (Symbol, Symbol) => LazyType) = { assert(!(name.toString endsWith "[]"), name) val clazz = owner.newClass(name) val module = owner.newModule(name.toTermName) @@ -67,7 +67,7 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => owner.info.decls enter clazz owner.info.decls enter module } - initClassModule(clazz, module, completer(clazz, module)) + initClassAndModule(clazz, module, completer(clazz, module)) (clazz, module) } @@ -75,7 +75,7 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => List(clazz, module, module.moduleClass) foreach (_ setInfo info) } - protected def initClassModule(clazz: Symbol, module: Symbol, completer: LazyType) = + protected def initClassAndModule(clazz: Symbol, module: Symbol, completer: LazyType) = setAllInfos(clazz, module, completer) /** The type completer for packages. @@ -118,7 +118,7 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => val loadingMirror = currentMirror.mirrorDefining(cls) val (clazz, module) = if (loadingMirror eq currentMirror) { - createClassModule(pkgClass, name.toTypeName, new TopClassCompleter(_, _)) + initAndEnterClassAndModule(pkgClass, name.toTypeName, new TopClassCompleter(_, _)) } else { val origOwner = loadingMirror.packageNameToScala(pkgClass.fullName) val clazz = origOwner.info decl name.toTypeName |